mkdb: deref the array to not rely on new perl versions
[gtk-doc.git] / gtkdoc-mkdb.in
blob1c25cb1322fbfaf6e81376c6045e7ec811c1173a
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 $SGML_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;
56 my %optctl = ('module' => \$MODULE,
57               'source-dir' => \@SOURCE_DIRS,
58               'source-suffixes' => \$SOURCE_SUFFIXES,
59               'ignore-files' => \$IGNORE_FILES,
60               'output-dir' => \$SGML_OUTPUT_DIR,
61               'tmpl-dir' => \$TMPL_DIR,
62               'version' => \$PRINT_VERSION,
63               'help' => \$PRINT_HELP,
64               'main-sgml-file' => \$MAIN_SGML_FILE,
65               'expand-content-files' => \$EXPAND_CONTENT_FILES,
66               'sgml-mode' => \$INLINE_MARKUP_MODE,
67               'xml-mode' => \$INLINE_MARKUP_MODE,
68               'default-stability' => \$DEFAULT_STABILITY,
69               'default-includes' => \$DEFAULT_INCLUDES,
70               'output-format' => \$OUTPUT_FORMAT,
71               'name-space' => \$NAME_SPACE,
72               'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
73               'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
74               );
75 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
76     "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version", "outputallsymbols",
77     "outputsymbolswithoutsince",
78     "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
79     "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
80     "output-format:s", "name-space:s");
82 if ($PRINT_VERSION) {
83     print "@VERSION@\n";
84     exit 0;
87 if (!$MODULE) {
88     $PRINT_HELP = 1;
91 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
92     && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
93     $PRINT_HELP = 1;
96 if ($PRINT_HELP) {
97     print <<EOF;
98 gtkdoc-mkdb version @VERSION@ - generate docbook files
100 --module=MODULE_NAME       Name of the doc module being parsed
101 --source-dir=DIRNAME       Directories which contain inline reference material
102 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
103 --ignore-files=FILES       Files or directories which should not be scanned
104                            May be used more than once for multiple directories
105 --output-dir=DIRNAME       Directory to put the generated DocBook files in
106 --tmpl-dir=DIRNAME         Directory in which template files may be found
107 --main-sgml-file=FILE      File containing the toplevel DocBook file.
108 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
109 --output-format=FORMAT     Format to use for the generated docbook, XML or SGML.
110 --{xml,sgml}-mode          Allow DocBook markup in inline documentation.
111 --default-stability=LEVEL  Specify default stability Level. Valid values are
112                            Stable, Unstable, or Private.
113 --default-includes=FILENAMES Specify default includes for section Synopsis
114 --name-space=NS            Omit namespace in index.
115 --version                  Print the version of this program
116 --help                     Print this help
118     exit 0;
121 my ($empty_element_end, $doctype_header);
123 # autodetect output format
124 if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
125     if (!$MAIN_SGML_FILE) {
126         if (-e "${MODULE}-docs.xml") {
127             $OUTPUT_FORMAT = "xml";
128         } else {
129             $OUTPUT_FORMAT = "sgml";
130         }
131     } else {
132         if ($MAIN_SGML_FILE =~ m/.*\.(.*ml)$/i) {
133             $OUTPUT_FORMAT = lc($1);
134         }
135     }
137 } else {
138     $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
141 #print "DEBUG: output-format: [$OUTPUT_FORMAT]\n";
143 if ($OUTPUT_FORMAT eq "xml") {
144     if (!$MAIN_SGML_FILE) {
145         # backwards compatibility
146         if (-e "${MODULE}-docs.sgml") {
147             $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
148         } else {
149             $MAIN_SGML_FILE = "${MODULE}-docs.xml";
150         }
151     }
152     $empty_element_end = "/>";
154     if (-e $MAIN_SGML_FILE) {
155         open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
156         $doctype_header = "";
157         while (<INPUT>) {
158             if (/^\s*<(book|chapter|article)/) {
159                 # check that the top-level tag or the doctype decl contain the xinclude namespace decl
160                 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
161                     $doctype_header = "";
162                 }
163                 last;
164             }
165             $doctype_header .= $_;
166         }
167         close(INPUT);
168         $doctype_header =~ s/<!DOCTYPE \w+/<!DOCTYPE refentry/;
169         # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
170         # FIXME: not sure if we can do this now, as people already work-around the problem
171         # $doctype_header =~ s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#g;
172     } else {
173         $doctype_header =
174 "<?xml version=\"1.0\"?>\n" .
175 "<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.3//EN\"\n" .
176 "               \"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd\"\n" .
177 "[\n" .
178 "  <!ENTITY % local.common.attrib \"xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'\">\n" .
179 "]>\n";
180     }
181 } else {
182     if (!$MAIN_SGML_FILE) {
183         $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
184     }
185     $empty_element_end = ">";
186     $doctype_header = "";
189 my $ROOT_DIR = ".";
191 # All the files are written in subdirectories beneath here.
192 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
194 # This is where we put all the DocBook output.
195 $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/$OUTPUT_FORMAT";
197 # This file contains the object hierarchy.
198 my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
200 # This file contains the interfaces.
201 my $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
203 # This file contains the prerequisites.
204 my $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
206 # This file contains signal arguments and names.
207 my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
209 # The file containing Arg information.
210 my $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
212 # These global arrays store information on signals. Each signal has an entry
213 # in each of these arrays at the same index, like a multi-dimensional array.
214 my @SignalObjects;        # The GtkObject which emits the signal.
215 my @SignalNames;        # The signal name.
216 my @SignalReturns;        # The return type.
217 my @SignalFlags;        # Flags for the signal
218 my @SignalPrototypes;        # The rest of the prototype of the signal handler.
220 # These global arrays store information on Args. Each Arg has an entry
221 # in each of these arrays at the same index, like a multi-dimensional array.
222 my @ArgObjects;                # The GtkObject which has the Arg.
223 my @ArgNames;                # The Arg name.
224 my @ArgTypes;                # The Arg type - gint, GtkArrowType etc.
225 my @ArgFlags;                # How the Arg can be used - readable/writable etc.
226 my @ArgNicks;                # The nickname of the Arg.
227 my @ArgBlurbs;          # Docstring of the Arg.
228 my @ArgDefaults;        # Default value of the Arg.
229 my @ArgRanges;                # The range of the Arg type
230 # These global hashes store declaration info keyed on a symbol name.
231 my %Declarations;
232 my %DeclarationTypes;
233 my %DeclarationConditional;
234 my %DeclarationOutput;
235 my %Deprecated;
236 my %Since;
237 my %StabilityLevel;
238 my %StructHasTypedef;
240 # These global hashes store the existing documentation.
241 my %SymbolDocs;
242 my %SymbolTypes;
243 my %SymbolParams;
244 my %SymbolSourceFile;
245 my %SymbolSourceLine;
247 # These global hashes store documentation scanned from the source files.
248 my %SourceSymbolDocs;
249 my %SourceSymbolParams;
250 my %SourceSymbolSourceFile;
251 my %SourceSymbolSourceLine;
253 # all documentation goes in here, so we can do coverage analysis
254 my %AllSymbols;
255 my %AllIncompleteSymbols;
256 my %AllUnusedSymbols;
257 my %AllDocumentedSymbols;
259 # Undeclared yet documented symbols
260 my %UndeclaredSymbols;
262 # These global arrays store GObject, subclasses and the hierarchy (also of
263 # non-object derived types).
264 my @Objects;
265 my @ObjectLevels;
266 my %ObjectRoots;
268 my %Interfaces;
269 my %Prerequisites;
271 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
272 # section they are defined
273 my %KnownSymbols;
274 my %SymbolSection;
275 my %SymbolSectionId;
277 # collects index entries
278 my %IndexEntriesFull;
279 my %IndexEntriesSince;
280 my %IndexEntriesDeprecated;
282 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
283 my %PreProcessorDirectives;
284 $PreProcessorDirectives{"assert"} = 1;
285 $PreProcessorDirectives{"define"} = 1;
286 $PreProcessorDirectives{"elif"} = 1;
287 $PreProcessorDirectives{"else"} = 1;
288 $PreProcessorDirectives{"endif"} = 1;
289 $PreProcessorDirectives{"error"} = 1;
290 $PreProcessorDirectives{"if"} = 1;
291 $PreProcessorDirectives{"ifdef"} = 1;
292 $PreProcessorDirectives{"ifndef"} = 1;
293 $PreProcessorDirectives{"include"} = 1;
294 $PreProcessorDirectives{"line"} = 1;
295 $PreProcessorDirectives{"pragma"} = 1;
296 $PreProcessorDirectives{"unassert"} = 1;
297 $PreProcessorDirectives{"undef"} = 1;
298 $PreProcessorDirectives{"warning"} = 1;
300 # remember used annotation (to write minimal glossary)
301 my %AnnotationsUsed;
303 my %AnnotationDefinition = (
304     # the GObjectIntrospection annotations are defined at:
305     # https://live.gnome.org/GObjectIntrospection/Annotations
306     'allow-none' => "NULL is ok, both for passing and for returning.",
307     'array' => "Parameter points to an array of items.",
308     'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
309     'attributes' => "Free-form key-value pairs.",
310     'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
311     'constructor' => "This symbol is a constructor, not a static method.",
312     'destroy' => "This parameter is a 'destroy_data', for callbacks.",
313     'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
314     'element-type' => "Generics and defining elements of containers and arrays.",
315     'error-domains' => "Typed errors. Similar to throws in Java.",
316     'foreign' => "This is a foreign struct.",
317     'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
318     'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
319     'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
320     'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
321     'method' => "This is a method",
322     'not-error' => "A GError parameter is not to be handled like a normal GError.",
323     'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
324     'out caller-allocates' => "Out parameter, where caller must allocate storage.",
325     'out callee-allocates' => "Out parameter, where caller must allocate storage.",
326     'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
327     'rename-to' => "Rename the original symbol's name to SYMBOL.",
328     'scope call' => "The callback is valid only during the call to the method.",
329     'scope async' => "The callback is valid until first called.",
330     'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
331     'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
332     'skip' => "Exposed in C code, not necessarily available in other languages.",
333     'transfer container' => "Free data container after the code is done.",
334     'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
335     'transfer full' => "Free data after the code is done.",
336     'transfer none' => "Don't free data after the code is done.",
337     'type' => "Override the parsed C type with given type.",
338     'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
339     'virtual' => "This is the invoker for a virtual method.",
340     'value' => "The specified value overrides the evaluated value of the constant.",
341     # Stability Level definition
342     # https://bugzilla.gnome.org/show_bug.cgi?id=170860
343     'Stable' => <<EOF,
344 The intention of a Stable interface is to enable arbitrary third parties to
345 develop applications to these interfaces, release them, and have confidence that
346 they will run on all minor releases of the product (after the one in which the
347 interface was introduced, and within the same major release). Even at a major
348 release, incompatible changes are expected to be rare, and to have strong
349 justifications.
351     'Unstable' => <<EOF,
352 Unstable interfaces are experimental or transitional. They are typically used to
353 give outside developers early access to new or rapidly changing technology, or
354 to provide an interim solution to a problem where a more general solution is
355 anticipated. No claims are made about either source or binary compatibility from
356 one minor release to the next.
358 The Unstable interface level is a warning that these interfaces are  subject to
359 change without warning and should not be used in unbundled products.
361 Given such caveats, customer impact need not be a factor when considering
362 incompatible changes to an Unstable interface in a major or minor release.
363 Nonetheless, when such changes are introduced, the changes should still be
364 mentioned in the release notes for the affected release.
366     'Private' => <<EOF
367 An interface that can be used within the GNOME stack itself, but that is not
368 documented for end-users.  Such functions should only be used in specified and
369 documented ways.
373 # Elements to consider non-block items in MarkDown parsing
374 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
375                                "emphasis" => 1,
376                                "envar" => 1,
377                                "filename" => 1,
378                                "firstterm" => 1,
379                                "function" => 1,
380                                "manvolnum" => 1,
381                                "option" => 1,
382                                "replaceable" => 1,
383                                "structname" => 1,
384                                "title" => 1,
385                                "varname" => 1 );
386 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
387                            "`" => 1,
388                            "*" => 1,
389                            "_" => 1,
390                            "{" => 1,
391                            "}" => 1,
392                            "[" => 1,
393                            "]" => 1,
394                            "(" => 1,
395                            ")" => 1,
396                            ">" => 1,
397                            "#" => 1,
398                            "+" => 1,
399                            "-" => 1,
400                            "." => 1,
401                            "!" => 1 );
402 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
403                                "%" => 1 );
405 # Create the root DocBook output directory if it doens't exist.
406 if (! -e $SGML_OUTPUT_DIR) {
407     mkdir ("$SGML_OUTPUT_DIR", 0777)
408         || die "Can't create directory: $SGML_OUTPUT_DIR";
411 # Function and other declaration output settings.
412 my $RETURN_TYPE_FIELD_WIDTH = 20;
413 my $SYMBOL_FIELD_WIDTH = 36;
414 my $MAX_SYMBOL_FIELD_WIDTH = 40;
415 my $SIGNAL_FIELD_WIDTH = 16;
416 my $PARAM_FIELD_COUNT = 2;
418 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
419 &ReadSignalsFile ($SIGNALS_FILE);
420 &ReadArgsFile ($ARGS_FILE);
421 &ReadObjectHierarchy;
422 &ReadInterfaces;
423 &ReadPrerequisites;
425 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
426 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
427     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
430 for my $dir (@SOURCE_DIRS) {
431     &ReadSourceDocumentation ($dir);
434 my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt");
436 # If any of the DocBook SGML files have changed, update the timestamp file (so
437 # it can be used for Makefile dependencies).
438 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
440     # try to detect the common prefix
441     # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
442     if ($NAME_SPACE eq "") {
443         $NAME_SPACE="";
444         my $pos=0;
445         my $ratio=0.0;
446         do {
447             my %prefix;
448             my $letter="";
449             foreach my $symbol (keys(%IndexEntriesFull)) {
450                 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
451                     if (length($symbol)>$pos) {
452                         $letter=substr($symbol,$pos,1);
453                         # stop prefix scanning
454                         if ($letter eq "_") {
455                             # stop on "_"
456                             last;
457                         }
458                         # Should we also stop on a uppercase char, if last was lowercase
459                         #   GtkWidget, if we have the 'W' and had the 't' before
460                         # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
461                         #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
462                         # need to recound each time as this is per symbol
463                         $prefix{uc($letter)}++;
464                     }
465                 }
466             }
467             if ($letter ne "" && $letter ne "_") {
468                 my $maxletter="";
469                 my $maxsymbols=0;
470                 foreach $letter (keys(%prefix)) {
471                     #print "$letter: $prefix{$letter}.\n";
472                     if ($prefix{$letter}>$maxsymbols) {
473                         $maxletter=$letter;
474                         $maxsymbols=$prefix{$letter};
475                     }
476                 }
477                 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
478                 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
479                 if ($ratio > 0.9) {
480                     # do another round
481                     $NAME_SPACE .= $maxletter;
482                 }
483                 $pos++;
484             }
485             else {
486                 $ratio=0.0;
487             }
488         } while ($ratio > 0.9);
489         #print "most symbols start with $NAME_SPACE\n";
490     }
492     &OutputIndexFull;
493     &OutputDeprecatedIndex;
494     &OutputSinceIndexes;
495     &OutputAnnotationGlossary;
497     open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
498         || die "Can't create $ROOT_DIR/sgml.stamp: $!";
499     print (TIMESTAMP "timestamp");
500     close (TIMESTAMP);
503 #############################################################################
504 # Function    : OutputObjectList
505 # Description : This outputs the alphabetical list of objects, in a columned
506 #                table.
507 #               FIXME: Currently this also outputs ancestor objects
508 #                which may not actually be in this module.
509 # Arguments   : none
510 #############################################################################
512 sub OutputObjectList {
513     my $cols = 3;
515     # FIXME: use $OUTPUT_FORMAT
516     # my $old_object_index = "$SGML_OUTPUT_DIR/object_index.$OUTPUT_FORMAT";
517     my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml";
518     my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new";
520     open (OUTPUT, ">$new_object_index")
521         || die "Can't create $new_object_index: $!";
523     if ($OUTPUT_FORMAT eq "xml") {
524         my $header = $doctype_header;
526         $header =~ s/<!DOCTYPE \w+/<!DOCTYPE informaltable/;
527         print (OUTPUT "$header");
528     }
530     print (OUTPUT <<EOF);
531 <informaltable pgwide="1" frame="none">
532 <tgroup cols="$cols">
533 <colspec colwidth="1*"${empty_element_end}
534 <colspec colwidth="1*"${empty_element_end}
535 <colspec colwidth="1*"${empty_element_end}
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) = @_;
575   
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;
580   
581   return $desc;
585 #############################################################################
586 # Function    : OutputSGML
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 OutputSGML {
595     my ($file) = @_;
597     #print "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             #print "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             #print "End of section: $title\n";
694             if ($num_symbols > 0) {
695                 # collect documents
696                 if ($OUTPUT_FORMAT eq "xml") {
697                     $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
698                 } else {
699                     $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n";
700                     $book_bottom .= "    &$section_id;\n";
701                 }
703                 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
704                     if ($section_includes) {
705                         &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
706                     }
707                     $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
708                 }
709                 if ($section_includes eq "") {
710                     $section_includes = $includes;
711                 }
713                  $signals_synop =~ s/^\n*//g;
714                  $signals_synop =~ s/\n+$/\n/g;
715                 if ($signals_synop ne '') {
716                     $signals_synop = <<EOF;
717 <refsect1 id="$section_id.signals" role="signal_proto">
718 <title role="signal_proto.title">Signals</title>
719 <informaltable frame="none">
720 <tgroup cols="3">
721 <colspec colname="signals_return" colwidth="150px"/>
722 <colspec colname="signals_name" colwidth="300px"/>
723 <colspec colname="signals_flags" colwidth="200px"/>
724 <tbody>
725 ${signals_synop}
726 </tbody>
727 </tgroup>
728 </informaltable>
729 </refsect1>
731                      $signals_desc = TrimTextBlock($signals_desc);
732                     $signals_desc  = <<EOF;
733 <refsect1 id="$section_id.signal-details" role="signals">
734 <title role="signals.title">Signal Details</title>
735 $signals_desc
736 </refsect1>
738                 }
740                 $args_synop =~ s/^\n*//g;
741                 $args_synop =~ s/\n+$/\n/g;
742                 if ($args_synop ne '') {
743                     $args_synop = <<EOF;
744 <refsect1 id="$section_id.properties" role="properties">
745 <title role="properties.title">Properties</title>
746 <informaltable frame="none">
747 <tgroup cols="3">
748 <colspec colname="properties_type" colwidth="150px"/>
749 <colspec colname="properties_name" colwidth="300px"/>
750 <colspec colname="properties_flags" colwidth="200px"/>
751 <tbody>
752 ${args_synop}
753 </tbody>
754 </tgroup>
755 </informaltable>
756 </refsect1>
758                      $args_desc = TrimTextBlock($args_desc);
759                     $args_desc  = <<EOF;
760 <refsect1 id="$section_id.property-details" role="property_details">
761 <title role="property_details.title">Property Details</title>
762 $args_desc
763 </refsect1>
765                 }
767                 $child_args_synop =~ s/^\n*//g;
768                 $child_args_synop =~ s/\n+$/\n/g;
769                 if ($child_args_synop ne '') {
770                     $args_synop .= <<EOF;
771 <refsect1 id="$section_id.child-properties" role="child_properties">
772 <title role="child_properties.title">Child Properties</title>
773 <informaltable frame="none">
774 <tgroup cols="3">
775 <colspec colname="child_properties_type" colwidth="150px"/>
776 <colspec colname="child_properties_name" colwidth="300px"/>
777 <colspec colname="child_properties_flags" colwidth="200px"/>
778 <tbody>
779 ${child_args_synop}
780 </tbody>
781 </tgroup>
782 </informaltable>
783 </refsect1>
785                      $child_args_desc = TrimTextBlock($child_args_desc);
786                      $args_desc .= <<EOF;
787 <refsect1 id="$section_id.child-property-details" role="child_property_details">
788 <title role="child_property_details.title">Child Property Details</title>
789 $child_args_desc
790 </refsect1>
792                 }
794                 $style_args_synop =~ s/^\n*//g;
795                 $style_args_synop =~ s/\n+$/\n/g;
796                 if ($style_args_synop ne '') {
797                     $args_synop .= <<EOF;
798 <refsect1 id="$section_id.style-properties" role="style_properties">
799 <title role="style_properties.title">Style Properties</title>
800 <informaltable frame="none">
801 <tgroup cols="3">
802 <colspec colname="style_properties_type" colwidth="150px"/>
803 <colspec colname="style_properties_name" colwidth="300px"/>
804 <colspec colname="style_properties_flags" colwidth="200px"/>
805 <tbody>
806 ${style_args_synop}
807 </tbody>
808 </tgroup>
809 </informaltable>
810 </refsect1>
812                      $style_args_desc = TrimTextBlock($style_args_desc);
813                     $args_desc .= <<EOF;
814 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
815 <title role="style_properties_details.title">Style Property Details</title>
816 $style_args_desc
817 </refsect1>
819                 }
821                 $hierarchy_str = &AddTreeLineArt(\@hierarchy) . "\n"; 
822                 if ($hierarchy_str ne "") {
823                     $hierarchy_str = <<EOF;
824 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
825 <title role="object_hierarchy.title">Object Hierarchy</title>
826 <screen>$hierarchy_str</screen>
827 </refsect1>
829                 }
831                  $interfaces =~ TrimTextBlock($interfaces);
832                 if ($interfaces ne "") {
833                     $interfaces = <<EOF;
834 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
835 <title role="impl_interfaces.title">Implemented Interfaces</title>
836 $interfaces
837 </refsect1>
839                 }
841                  $implementations = TrimTextBlock($implementations);
842                 if ($implementations ne "") {
843                     $implementations = <<EOF;
844 <refsect1 id="$section_id.implementations" role="implementations">
845 <title role="implementations.title">Known Implementations</title>
846 $implementations
847 </refsect1>
849                 }
851                  $prerequisites = TrimTextBlock($prerequisites);
852                 if ($prerequisites ne "") {
853                     $prerequisites = <<EOF;
854 <refsect1 id="$section_id.prerequisites" role="prerequisites">
855 <title role="prerequisites.title">Prerequisites</title>
856 $prerequisites
857 </refsect1>
859                 }
861                  $derived = TrimTextBlock($derived);
862                 if ($derived ne "") {
863                     $derived = <<EOF;
864 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
865 <title role="derived_interfaces.title">Known Derived Interfaces</title>
866 $derived
867 </refsect1>
869                 }
871                 $functions_synop =~ s/^\n*//g;
872                 $functions_synop =~ s/\n+$/\n/g;
873                 if ($functions_synop ne '') {
874                   $functions_synop = <<EOF;
875 <refsect1 id="$section_id.functions" role="functions_proto">
876 <title role="functions_proto.title">Functions</title>
877 <informaltable pgwide="1" frame="none">
878 <tgroup cols="2">
879 <colspec colname="functions_return" colwidth="150px"/>
880 <colspec colname="functions_name"/>
881 <tbody>
882 ${functions_synop}
883 </tbody>
884 </tgroup>
885 </informaltable>
886 </refsect1>
888                 }
890                 $other_synop =~ s/^\n*//g;
891                 $other_synop =~ s/\n+$/\n/g;
892                 if ($other_synop ne '') {
893                   $other_synop = <<EOF;
894 <refsect1 id="$section_id.other" role="other_proto">
895 <title role="other_proto.title">Types and Values</title>
896 <informaltable role="enum_members_table" pgwide="1" frame="none">
897 <tgroup cols="2">
898 <colspec colname="name" colwidth="150px"/>
899 <colspec colname="description"/>
900 <tbody>
901 ${other_synop}
902 </tbody>
903 </tgroup>
904 </informaltable>
905 </refsect1>
907                 }
909                 my $file_changed = &OutputSGMLFile ($filename, $title, $section_id,
910                                                     $section_includes,
911                                                     \$functions_synop, \$other_synop,
912                                                     \$functions_details, \$other_details,
913                                                     \$signals_synop, \$signals_desc,
914                                                     \$args_synop, \$args_desc,
915                                                     \$hierarchy_str, \$interfaces,
916                                                     \$implementations,
917                                                     \$prerequisites, \$derived,
918                                                     \@file_objects);
919                 if ($file_changed) {
920                     $changed = 1;
921                 }
922             }
923             $title = "";
924             $section_id = "";
925             $subsection = "";
926             $in_section = 0;
927             $section_includes = "";
928             $functions_synop = "";
929             $other_synop = "";
930             $functions_details = "";
931             $other_details = "";
932             $signals_synop = "";
933             $signals_desc = "";
934             $args_synop = "";
935             $child_args_synop = "";
936             $style_args_synop = "";
937             $args_desc = "";
938             $child_args_desc = "";
939             $style_args_desc = "";
940             $hierarchy_str = "";
941             @hierarchy = ();
942             $interfaces = "";
943             $implementations = "";
944             $prerequisites = "";
945             $derived = "";
947         } elsif (m/^(\S+)/) {
948             my $symbol = $1;
949             @TRACE@("  Symbol: $symbol in subsection: $subsection\n");
951             # check for duplicate entries
952             if (! defined $symbol_def_line{$symbol}) {
953                 my $declaration = $Declarations{$symbol};
954                 if (defined ($declaration)) {
955                     if (&CheckIsObject ($symbol)) {
956                         push @file_objects, $symbol;
957                     }
958                     # We don't want standard macros/functions of GObjects,
959                     # or private declarations.
960                     if ($subsection ne "Standard" && $subsection ne "Private") {
961                         my ($synop, $desc) = &OutputDeclaration ($symbol,
962                                                                  $declaration);
963                         my $type = $DeclarationTypes {$symbol};
964         
965                         if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
966                           $functions_synop .= $synop;
967                           $functions_details .= $desc;
968                         } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
969                           $functions_synop .= $synop;
970                           $functions_details .= $desc;
971                         } else {
972                           $other_synop .= $synop;
973                           $other_details .= $desc;
974                         }
975                     }
976                     my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
977                     my ($arg_synop, $child_arg_synop, $style_arg_synop,
978                         $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
979                     my $ifaces = &GetInterfaces ($symbol);
980                     my $impls = &GetImplementations ($symbol);
981                     my $prereqs = &GetPrerequisites ($symbol);
982                     my $der = &GetDerived ($symbol);
983                     @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
985                     $signals_synop .= $sig_synop;
986                     $signals_desc .= $sig_desc;
987                     $args_synop .= $arg_synop;
988                     $child_args_synop .= $child_arg_synop;
989                     $style_args_synop .= $style_arg_synop;
990                     $args_desc .= $arg_desc;
991                     $child_args_desc .= $child_arg_desc;
992                     $style_args_desc .= $style_arg_desc;
993                     $interfaces .= $ifaces;
994                     $implementations .= $impls;
995                     $prerequisites .= $prereqs;
996                     $derived .= $der;
998                     # Note that the declaration has been output.
999                     $DeclarationOutput{$symbol} = 1;
1000                 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
1001                     $UndeclaredSymbols{$symbol} = 1;
1002                     &LogWarning ($file, $., "No declaration found for $symbol.");
1003                 }
1004                 $num_symbols++;
1005                 $symbol_def_line{$symbol}=$.;
1007                 if ($section_id eq "") {
1008                     if($title eq "" && $filename eq "") {
1009                         &LogWarning ($file, $., "Section has no title and no file.");
1010                     }
1011                     # FIXME: one of those would be enough
1012                     # filename should be an internal detail for gtk-doc
1013                     if ($title eq "") {
1014                         $title = $filename;
1015                     } elsif ($filename eq "") {
1016                         $filename = $title;
1017                     }
1018                     $filename =~ s/\s/_/g;
1020                     $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1021                     if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1022                         # Remove trailing blanks and use as is
1023                         $section_id =~ s/\s+$//;
1024                     } elsif (&CheckIsObject ($title)) {
1025                         # GObjects use their class name as the ID.
1026                         $section_id = &CreateValidSGMLID ($title);
1027                     } else {
1028                         $section_id = &CreateValidSGMLID ("$MODULE-$title");
1029                     }
1030                 }
1031                 $SymbolSection{$symbol}=$title;
1032                 $SymbolSectionId{$symbol}=$section_id;
1033             }
1034             else {
1035                 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1036                     "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1037             }
1038         }
1039     }
1040     close (INPUT);
1042     &OutputMissingDocumentation;
1043     &OutputUndeclaredSymbols;
1044     &OutputUnusedSymbols;
1046     if ($OUTPUT_ALL_SYMBOLS) {
1047         &OutputAllSymbols;
1048     }
1049     if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1050         &OutputSymbolsWithoutSince;
1051     }
1053     for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1054         my $file_changed = &OutputExtraFile ($filename);
1055         if ($file_changed) {
1056             $changed = 1;
1057         }
1058     }
1060     &OutputBook ($book_top, $book_bottom);
1062     return $changed;
1065 #############################################################################
1066 # Function    : OutputIndex
1067 # Description : This writes an indexlist that can be included into the main-
1068 #               document into an <index> tag.
1069 #############################################################################
1071 sub OutputIndex {
1072     my ($basename, $apiindexref ) = @_;
1073     my %apiindex = %{$apiindexref};
1074     my $old_index = "$SGML_OUTPUT_DIR/$basename.xml";
1075     my $new_index = "$SGML_OUTPUT_DIR/$basename.new";
1076     my $lastletter = " ";
1077     my $divopen = 0;
1078     my $symbol;
1079     my $short_symbol;
1081     open (OUTPUT, ">$new_index")
1082         || die "Can't create $new_index";
1084     my $header = $doctype_header;
1085     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE indexdiv/;
1087     print (OUTPUT "$header<indexdiv>\n");
1089     #print "generate $basename index (".%apiindex." entries)\n";
1091     # do a case insensitive sort while chopping off the prefix
1092     foreach my $hash (
1093         sort { $$a{criteria} cmp $$b{criteria} }
1094         map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1095         keys %apiindex) {
1097         $symbol = $$hash{original};
1098         if (defined($$hash{short})) {
1099             $short_symbol = $$hash{short};
1100         } else {
1101             $short_symbol = $symbol;
1102         }
1104         # generate a short symbol description
1105         my $symbol_desc = "";
1106         my $symbol_section = "";
1107         my $symbol_section_id = "";
1108         my $symbol_type = "";
1109         if (defined($DeclarationTypes{$symbol})) {
1110           $symbol_type = lc($DeclarationTypes{$symbol});
1111         }
1112         if ($symbol_type eq "") {
1113             #print "trying symbol $symbol\n";
1114             if ($symbol =~ m/(.*)::(.*)/) {
1115                 my $oname = $1;
1116                 my $osym = $2;
1117                 my $i;
1118                 #print "  trying object signal ${oname}:$osym in ".$#SignalNames." signals\n";
1119                 for ($i = 0; $i <= $#SignalNames; $i++) {
1120                     if ($SignalNames[$i] eq $osym) {
1121                         $symbol_type = "object signal";
1122                         if (defined($SymbolSection{$oname})) {
1123                            $symbol_section = $SymbolSection{$oname};
1124                            $symbol_section_id = $SymbolSectionId{$oname};
1125                         }
1126                         last;
1127                     }
1128                 }
1129             } elsif ($symbol =~ m/(.*):(.*)/) {
1130                 my $oname = $1;
1131                 my $osym = $2;
1132                 my $i;
1133                 #print "  trying object property ${oname}::$osym in ".$#ArgNames." properties\n";
1134                 for ($i = 0; $i <= $#ArgNames; $i++) {
1135                     #print "    ".$ArgNames[$i]."\n";
1136                     if ($ArgNames[$i] eq $osym) {
1137                         $symbol_type = "object property";
1138                         if (defined($SymbolSection{$oname})) {
1139                            $symbol_section = $SymbolSection{$oname};
1140                            $symbol_section_id = $SymbolSectionId{$oname};
1141                         }
1142                         last;
1143                     }
1144                 }
1145             }
1146         } else {
1147            if (defined($SymbolSection{$symbol})) {
1148                $symbol_section = $SymbolSection{$symbol};
1149                $symbol_section_id = $SymbolSectionId{$symbol};
1150            }
1151         }
1152         if ($symbol_type ne "") {
1153            $symbol_desc=", $symbol_type";
1154            if ($symbol_section ne "") {
1155                $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1156                #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1157            }
1158         }
1160         my $curletter = uc(substr($short_symbol,0,1));
1161         my $id = $apiindex{$symbol};
1163         #print "  add symbol $symbol with $id to index in section $curletter\n";
1165         if ($curletter ne $lastletter) {
1166             $lastletter = $curletter;
1168             if ($divopen == 1) {
1169                 print (OUTPUT "</indexdiv>\n");
1170             }
1171             print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1172             $divopen = 1;
1173         }
1175         print (OUTPUT <<EOF);
1176 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1178     }
1180     if ($divopen == 1) {
1181         print (OUTPUT "</indexdiv>\n");
1182     }
1183     print (OUTPUT "</indexdiv>\n");
1184     close (OUTPUT);
1186     &UpdateFileIfChanged ($old_index, $new_index, 0);
1190 #############################################################################
1191 # Function    : OutputIndexFull
1192 # Description : This writes the full api indexlist that can be included into the
1193 #               main document into an <index> tag.
1194 #############################################################################
1196 sub OutputIndexFull {
1197     &OutputIndex ("api-index-full", \%IndexEntriesFull);
1201 #############################################################################
1202 # Function    : OutputDeprecatedIndex
1203 # Description : This writes the deprecated api indexlist that can be included
1204 #               into the main document into an <index> tag.
1205 #############################################################################
1207 sub OutputDeprecatedIndex {
1208     &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1212 #############################################################################
1213 # Function    : OutputSinceIndexes
1214 # Description : This writes the 'since' api indexlists that can be included into
1215 #               the main document into an <index> tag.
1216 #############################################################################
1218 sub OutputSinceIndexes {
1219     my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1221     foreach my $version (@sinces) {
1222         #print "Since : [$version]\n";
1223         # TODO make filtered hash
1224         #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1225         my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1227         &OutputIndex ("api-index-$version", \%index);
1228     }
1231 #############################################################################
1232 # Function    : OutputAnnotationGlossary
1233 # Description : This writes a glossary of the used annotation terms into a
1234 #               separate glossary file that can be included into the main
1235 #               document.
1236 #############################################################################
1238 sub OutputAnnotationGlossary {
1239     my $old_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.xml";
1240     my $new_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.new";
1241     my $lastletter = " ";
1242     my $divopen = 0;
1244     # if there are no annotations used return
1245     return if (! keys(%AnnotationsUsed));
1247     # add acronyms that are referenced from acronym text
1248 rerun:
1249     foreach my $annotation (keys(%AnnotationsUsed)) {
1250         if(defined($AnnotationDefinition{$annotation})) {
1251             if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1252                 if (!exists($AnnotationsUsed{$1})) {
1253                     $AnnotationsUsed{$1} = 1;
1254                     goto rerun;
1255                 }
1256             }
1257         }
1258     }
1260     open (OUTPUT, ">$new_glossary")
1261         || die "Can't create $new_glossary";
1263     my $header = $doctype_header;
1264     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE glossary/;
1266     print (OUTPUT  <<EOF);
1267 $header
1268 <glossary id="annotation-glossary">
1269   <title>Annotation Glossary</title>
1272     foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1273         if(defined($AnnotationDefinition{$annotation})) {
1274             my $def = $AnnotationDefinition{$annotation};
1275             my $curletter = uc(substr($annotation,0,1));
1277             if ($curletter ne $lastletter) {
1278                 $lastletter = $curletter;
1280                 if ($divopen == 1) {
1281                     print (OUTPUT "</glossdiv>\n");
1282                 }
1283                 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1284                 $divopen = 1;
1285             }
1286             print (OUTPUT <<EOF);
1287     <glossentry>
1288       <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1289       <glossdef>
1290         <para>$def</para>
1291       </glossdef>
1292     </glossentry>
1294         }
1295     }
1297     if ($divopen == 1) {
1298         print (OUTPUT "</glossdiv>\n");
1299     }
1300     print (OUTPUT "</glossary>\n");
1301     close (OUTPUT);
1303     &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1306 #############################################################################
1307 # Function    : ReadKnownSymbols
1308 # Description : This collects the names of non-private symbols from the
1309 #               $MODULE-sections.txt file.
1310 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
1311 #                the functions/macros/structs etc. being documented, organised
1312 #                into sections and subsections.
1313 #############################################################################
1315 sub ReadKnownSymbols {
1316     my ($file) = @_;
1318     my $subsection = "";
1320     #print "Reading: $file\n";
1321     open (INPUT, $file)
1322         || die "Can't open $file: $!";
1324     while (<INPUT>) {
1325         if (m/^#/) {
1326             next;
1328         } elsif (m/^<SECTION>/) {
1329             $subsection = "";
1331         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1332             $subsection = $1;
1334         } elsif (m/^<SUBSECTION>/) {
1335             next;
1337         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1338             next;
1340         } elsif (m/^<FILE>(.*)<\/FILE>/) {
1341             $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1342             $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1343             next;
1345         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1346             next;
1348         } elsif (m/^<\/SECTION>/) {
1349             next;
1351         } elsif (m/^(\S+)/) {
1352             my $symbol = $1;
1354             if ($subsection ne "Standard" && $subsection ne "Private") {
1355                 $KnownSymbols{$symbol} = 1;
1356             }
1357             else {
1358                 $KnownSymbols{$symbol} = 0;
1359             }
1360         }
1361     }
1362     close (INPUT);
1366 #############################################################################
1367 # Function    : OutputDeclaration
1368 # Description : Returns the synopsis and detailed description DocBook
1369 #                describing one function/macro etc.
1370 # Arguments   : $symbol - the name of the function/macro begin described.
1371 #                $declaration - the declaration of the function/macro.
1372 #############################################################################
1374 sub OutputDeclaration {
1375     my ($symbol, $declaration) = @_;
1377     my $type = $DeclarationTypes {$symbol};
1378     if ($type eq 'MACRO') {
1379         return &OutputMacro ($symbol, $declaration);
1380     } elsif ($type eq 'TYPEDEF') {
1381         return &OutputTypedef ($symbol, $declaration);
1382     } elsif ($type eq 'STRUCT') {
1383         return &OutputStruct ($symbol, $declaration);
1384     } elsif ($type eq 'ENUM') {
1385         return &OutputEnum ($symbol, $declaration);
1386     } elsif ($type eq 'UNION') {
1387         return &OutputUnion ($symbol, $declaration);
1388     } elsif ($type eq 'VARIABLE') {
1389         return &OutputVariable ($symbol, $declaration);
1390     } elsif ($type eq 'FUNCTION') {
1391         return &OutputFunction ($symbol, $declaration, $type);
1392     } elsif ($type eq 'USER_FUNCTION') {
1393         return &OutputFunction ($symbol, $declaration, $type);
1394     } else {
1395         die "Unknown symbol type";
1396     }
1400 #############################################################################
1401 # Function    : OutputSymbolTraits
1402 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1403 # Arguments   : $symbol - the name of the function/macro begin described.
1404 #############################################################################
1406 sub OutputSymbolTraits {
1407     my ($symbol) = @_;
1408     my $desc = "";
1410     if (exists $Since{$symbol}) {
1411         $desc .= "<para role=\"since\">Since $Since{$symbol}</para>";
1412     }
1413     if (exists $StabilityLevel{$symbol}) {
1414         my $stability = $StabilityLevel{$symbol};
1415         $AnnotationsUsed{$stability} = 1;
1416         $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1417     }
1418     return $desc;
1421 #############################################################################
1422 # Function    : Output{Symbol,Section}ExtraLinks
1423 # Description : Returns extralinks for the symbol (if enabled).
1424 # Arguments   : $symbol - the name of the function/macro begin described.
1425 #############################################################################
1427 sub uri_escape {
1428     my $text = $_[0];
1429     return undef unless defined $text;
1431     # Build a char to hex map
1432     my %escapes = ();
1433     for (0..255) {
1434             $escapes{chr($_)} = sprintf("%%%02X", $_);
1435     }
1437     # Default unsafe characters.  RFC 2732 ^(uric - reserved)
1438     $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1440     return $text;
1443 sub OutputSymbolExtraLinks {
1444     my ($symbol) = @_;
1445     my $desc = "";
1447     if (0) { # NEW FEATURE: needs configurability
1448     my $sstr = &uri_escape($symbol);
1449     my $mstr = &uri_escape($MODULE);
1450     $desc .= <<EOF;
1451 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1452 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$sstr">edit documentation</ulink>
1454     }
1455     return $desc;
1458 sub OutputSectionExtraLinks {
1459     my ($symbol,$docsymbol) = @_;
1460     my $desc = "";
1462     if (0) { # NEW FEATURE: needs configurability
1463     my $sstr = &uri_escape($symbol);
1464     my $mstr = &uri_escape($MODULE);
1465     my $dsstr = &uri_escape($docsymbol);
1466     $desc .= <<EOF;
1467 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1468 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$dsstr">edit documentation</ulink>
1470     }
1471     return $desc;
1475 #############################################################################
1476 # Function    : OutputMacro
1477 # Description : Returns the synopsis and detailed description of a macro.
1478 # Arguments   : $symbol - the macro.
1479 #                $declaration - the declaration of the macro.
1480 #############################################################################
1482 sub OutputMacro {
1483     my ($symbol, $declaration) = @_;
1484     my $id = &CreateValidSGMLID ($symbol);
1485     my $condition = &MakeConditionDescription ($symbol);
1486     my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1487     my $desc;
1489     my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1490     my $title = $symbol . (@fields ? "()" : "");
1492     $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1493     $desc .= MakeIndexterms($symbol, $id);
1494     $desc .= "\n";
1495     $desc .= OutputSymbolExtraLinks($symbol);
1497     if (@fields) {
1498         $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1499     }
1500     $synop .= "</entry></row>\n";
1502     # Don't output the macro definition if is is a conditional macro or it
1503     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1504     # longer than 2 lines, otherwise we get lots of complicated macros like
1505     # g_assert.
1506     if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1507         && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1508         my $decl_out = &CreateValidSGML ($declaration);
1509         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1510     } else {
1511         $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1512         if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1513             my $args = $1;
1514             my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1515             # Align each line so that if should all line up OK.
1516             $args =~ s/\n/\n$pad/gm;
1517             $desc .= &CreateValidSGML ($args);
1518         }
1519         $desc .= "</programlisting>\n";
1520     }
1522     $desc .= &MakeDeprecationNote($symbol);
1524     my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1525     my $parameters_output = 0;
1527     if (defined ($SymbolDocs{$symbol})) {
1528         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1530         # Try to insert the parameter table at the author's desired position.
1531         # Otherwise we need to tag it onto the end.
1532         if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
1533           $parameters_output = 1;
1534         }
1535         $desc .= $symbol_docs;
1536     }
1538     if ($parameters_output == 0) {
1539         $desc .= $parameters;
1540     }
1542     $desc .= OutputSymbolTraits ($symbol);
1543     $desc .= "</refsect2>\n";
1544     return ($synop, $desc);
1548 #############################################################################
1549 # Function    : OutputTypedef
1550 # Description : Returns the synopsis and detailed description of a typedef.
1551 # Arguments   : $symbol - the typedef.
1552 #                $declaration - the declaration of the typedef,
1553 #                  e.g. 'typedef unsigned int guint;'
1554 #############################################################################
1556 sub OutputTypedef {
1557     my ($symbol, $declaration) = @_;
1558     my $id = &CreateValidSGMLID ($symbol);
1559     my $condition = &MakeConditionDescription ($symbol);
1560     my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1561     my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1563     $desc .= MakeIndexterms($symbol, $id);
1564     $desc .= "\n";
1565     $desc .= OutputSymbolExtraLinks($symbol);
1567     if (!defined ($DeclarationConditional{$symbol})) {
1568         my $decl_out = &CreateValidSGML ($declaration);
1569         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1570     }
1572     $desc .= &MakeDeprecationNote($symbol);
1574     if (defined ($SymbolDocs{$symbol})) {
1575         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1576     }
1577     $desc .= OutputSymbolTraits ($symbol);
1578     $desc .= "</refsect2>\n";
1579     return ($synop, $desc);
1583 #############################################################################
1584 # Function    : OutputStruct
1585 # Description : Returns the synopsis and detailed description of a struct.
1586 #                We check if it is a object struct, and if so we only output
1587 #                parts of it that are noted as public fields.
1588 #                We also use a different SGML ID for object structs, since the
1589 #                original ID is used for the entire RefEntry.
1590 # Arguments   : $symbol - the struct.
1591 #                $declaration - the declaration of the struct.
1592 #############################################################################
1594 sub OutputStruct {
1595     my ($symbol, $declaration) = @_;
1597     my $is_gtype = 0;
1598     my $default_to_public = 1;
1599     if (&CheckIsObject ($symbol)) {
1600         #print "Found struct gtype: $symbol\n";
1601         $is_gtype = 1;
1602         $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1603     }
1605     my $id;
1606     my $condition;
1607     if ($is_gtype) {
1608         $id = &CreateValidSGMLID ($symbol . "_struct");
1609         $condition = &MakeConditionDescription ($symbol . "_struct");
1610     } else {
1611         $id = &CreateValidSGMLID ($symbol);
1612         $condition = &MakeConditionDescription ($symbol);
1613     }
1615     # Determine if it is a simple struct or it also has a typedef.
1616     my $has_typedef = 0;
1617     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1618       $has_typedef = 1;
1619     }
1621     my $type_output;
1622     my $desc;
1623     if ($has_typedef) {
1624         # For structs with typedefs we just output the struct name.
1625         $type_output = "";
1626         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1627     } else {
1628         $type_output = "struct";
1629         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1630     }
1631     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1633     $desc .= MakeIndexterms($symbol, $id);
1634     $desc .= "\n";
1635     $desc .= OutputSymbolExtraLinks($symbol);
1637     # Form a pretty-printed, private-data-removed form of the declaration
1639     my $decl_out = "";
1640     if ($declaration =~ m/^\s*$/) {
1641         #print "Found opaque struct: $symbol\n";
1642         $decl_out = "typedef struct _$symbol $symbol;";
1643     } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1644         #print "Found opaque struct: $symbol\n";
1645         $decl_out = "struct $symbol;";
1646     } else {
1647         my $public = $default_to_public;
1648         my $new_declaration = "";
1649         my $decl_line;
1650         my $decl = $declaration;
1652         if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1653             my $struct_contents = $2;
1655             foreach $decl_line (split (/\n/, $struct_contents)) {
1656                 #print "Struct line: $decl_line\n";
1657                 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1658                     $public = 1;
1659                 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1660                     $public = 0;
1661                 } elsif ($public) {
1662                     $new_declaration .= $decl_line . "\n";
1663                 }
1664             }
1666             if ($new_declaration) {
1667                 # Strip any blank lines off the ends.
1668                 $new_declaration =~ s/^\s*\n//;
1669                 $new_declaration =~ s/\n\s*$/\n/;
1671                 if ($has_typedef) {
1672                     $decl_out = "typedef struct {\n" . $new_declaration
1673                       . "} $symbol;\n";
1674                 } else {
1675                     $decl_out = "struct $symbol {\n" . $new_declaration
1676                       . "};\n";
1677                 }
1678             }
1679         } else {
1680             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1681                 "Couldn't parse struct:\n$declaration");
1682         }
1684         # If we couldn't parse the struct or it was all private, output an
1685         # empty struct declaration.
1686         if ($decl_out eq "") {
1687             if ($has_typedef) {
1688                 $decl_out = "typedef struct _$symbol $symbol;";
1689             } else {
1690                 $decl_out = "struct $symbol;";
1691             }
1692         }
1693     }
1695     $decl_out = &CreateValidSGML ($decl_out);
1696     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1698     $desc .= &MakeDeprecationNote($symbol);
1700     if (defined ($SymbolDocs{$symbol})) {
1701         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1702     }
1704     # Create a table of fields and descriptions
1706     # FIXME: Inserting &#160's into the produced type declarations here would
1707     #        improve the output in most situations ... except for function
1708     #        members of structs!
1709     my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1710                                         0, \&MakeXRef,
1711                                         sub {
1712                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1713                                         });
1714     my $params = $SymbolParams{$symbol};
1716     # If no parameters are filled in, we don't generate the description
1717     # table, for backwards compatibility.
1719     my $found = 0;
1720     if (defined $params) {
1721         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1722             if ($params->[$i] =~ /\S/) {
1723                 $found = 1;
1724                 last;
1725             }
1726         }
1727     }
1729     if ($found) {
1730         my %field_descrs = @$params;
1731         my $missing_parameters = "";
1732         my $unused_parameters = "";
1734         $desc .= <<EOF;
1735 <refsect3 role="struct_members">\n<title>Members</title>
1736 <informaltable role="struct_members_table" pgwide="1" frame="none">
1737 <tgroup cols="3">
1738 <colspec colname="struct_members_name" colwidth="300px"/>
1739 <colspec colname="struct_members_description"/>
1740 <colspec colname="struct_members_annotations" colwidth="200px"/>
1741 <tbody>
1744         while (@fields) {
1745             my $field_name = shift @fields;
1746             my $text = shift @fields;
1747             my $field_descr = $field_descrs{$field_name};
1748             my $param_annotations = "";
1750             $desc .= "<row><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1751             if (defined $field_descr) {
1752                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1753                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1754                 # trim
1755                 $field_descr =~ s/^(\s|\n)+//msg;
1756                 $field_descr =~ s/(\s|\n)+$//msg;
1757                 $desc .= "<listitem>$field_descr</listitem>\n";
1758                 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1759                 delete $field_descrs{$field_name};
1760             } else {
1761                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1762                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1763                 if ($missing_parameters ne "") {
1764                   $missing_parameters .= ", ".$field_name;
1765                 } else {
1766                     $missing_parameters = $field_name;
1767                 }
1768                 $desc .= "<entry /><entry />\n";
1769             }
1770             $desc .= "</row>\n";
1771         }
1772         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1773         foreach my $field_name (keys %field_descrs) {
1774             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1775                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1776             if ($unused_parameters ne "") {
1777               $unused_parameters .= ", ".$field_name;
1778             } else {
1779                $unused_parameters = $field_name;
1780             }
1781         }
1783         # remember missing/unused parameters (needed in tmpl-free build)
1784         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1785             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1786         }
1787         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1788             $AllUnusedSymbols{$symbol}=$unused_parameters;
1789         }
1790     }
1791     else {
1792         if (scalar(@fields) > 0) {
1793             if (! exists ($AllIncompleteSymbols{$symbol})) {
1794                 $AllIncompleteSymbols{$symbol}="<items>";
1795                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1796                     "Field descriptions for struct $symbol are missing in source code comment block.");
1797                 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1798             }
1799         }
1800     }
1802     $desc .= OutputSymbolTraits ($symbol);
1803     $desc .= "</refsect2>\n";
1804     return ($synop, $desc);
1808 #############################################################################
1809 # Function    : OutputUnion
1810 # Description : Returns the synopsis and detailed description of a union.
1811 # Arguments   : $symbol - the union.
1812 #                $declaration - the declaration of the union.
1813 #############################################################################
1815 sub OutputUnion {
1816     my ($symbol, $declaration) = @_;
1818     my $is_gtype = 0;
1819     if (&CheckIsObject ($symbol)) {
1820         @TRACE@("Found union gtype: $symbol\n");
1821         $is_gtype = 1;
1822     }
1824     my $id;
1825     my $condition;
1826     if ($is_gtype) {
1827         $id = &CreateValidSGMLID ($symbol . "_union");
1828         $condition = &MakeConditionDescription ($symbol . "_union");
1829     } else {
1830         $id = &CreateValidSGMLID ($symbol);
1831         $condition = &MakeConditionDescription ($symbol);
1832     }
1834     # Determine if it is a simple struct or it also has a typedef.
1835     my $has_typedef = 0;
1836     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1837       $has_typedef = 1;
1838     }
1840     my $type_output;
1841     my $desc;
1842     if ($has_typedef) {
1843         # For unions with typedefs we just output the union name.
1844         $type_output = "";
1845         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1846     } else {
1847         $type_output = "union";
1848         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1849     }
1850     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1852     $desc .= MakeIndexterms($symbol, $id);
1853     $desc .= "\n";
1854     $desc .= OutputSymbolExtraLinks($symbol);
1855     $desc .= &MakeDeprecationNote($symbol);
1857     if (defined ($SymbolDocs{$symbol})) {
1858         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1859     }
1861     # Create a table of fields and descriptions
1863     # FIXME: Inserting &#160's into the produced type declarations here would
1864     #        improve the output in most situations ... except for function
1865     #        members of structs!
1866     my @fields = ParseStructDeclaration($declaration, 0,
1867                                         0, \&MakeXRef,
1868                                         sub {
1869                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1870                                         });
1871     my $params = $SymbolParams{$symbol};
1873     # If no parameters are filled in, we don't generate the description
1874     # table, for backwards compatibility
1876     my $found = 0;
1877     if (defined $params) {
1878         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1879             if ($params->[$i] =~ /\S/) {
1880                 $found = 1;
1881                 last;
1882             }
1883         }
1884     }
1886     if ($found) {
1887         my %field_descrs = @$params;
1888         my $missing_parameters = "";
1889         my $unused_parameters = "";
1891         $desc .= <<EOF;
1892 <refsect3 role="union_members">\n<title>Members</title>
1893 <informaltable role="union_members_table" pgwide="1" frame="none">
1894 <tgroup cols="3">
1895 <colspec colname="union_members_name" colwidth="300px"/>
1896 <colspec colname="union_members_description"/>
1897 <colspec colname="union_members_annotations" colwidth="200px"/>
1898 <tbody>
1901         while (@fields) {
1902             my $field_name = shift @fields;
1903             my $text = shift @fields;
1904             my $field_descr = $field_descrs{$field_name};
1905             my $param_annotations = "";
1907             $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1908             if (defined $field_descr) {
1909                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1910                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1912                 # trim
1913                 $field_descr =~ s/^(\s|\n)+//msg;
1914                 $field_descr =~ s/(\s|\n)+$//msg;
1915                 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1916                 delete $field_descrs{$field_name};
1917             } else {
1918                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1919                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1920                 if ($missing_parameters ne "") {
1921                     $missing_parameters .= ", ".$field_name;
1922                 } else {
1923                     $missing_parameters = $field_name;
1924                 }
1925                 $desc .= "<entry /><entry />\n";
1926             }
1927             $desc .= "</row>\n";
1928         }
1929         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1930         foreach my $field_name (keys %field_descrs) {
1931             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1932                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1933             if ($unused_parameters ne "") {
1934               $unused_parameters .= ", ".$field_name;
1935             } else {
1936                $unused_parameters = $field_name;
1937             }
1938         }
1940         # remember missing/unused parameters (needed in tmpl-free build)
1941         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1942             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1943         }
1944         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1945             $AllUnusedSymbols{$symbol}=$unused_parameters;
1946         }
1947     }
1948     else {
1949         if (scalar(@fields) > 0) {
1950             if (! exists ($AllIncompleteSymbols{$symbol})) {
1951                 $AllIncompleteSymbols{$symbol}="<items>";
1952                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1953                     "Field descriptions for union $symbol are missing in source code comment block.");
1954                 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1955             }
1956         }
1957     }
1959     $desc .= OutputSymbolTraits ($symbol);
1960     $desc .= "</refsect2>\n";
1961     return ($synop, $desc);
1965 #############################################################################
1966 # Function    : OutputEnum
1967 # Description : Returns the synopsis and detailed description of a enum.
1968 # Arguments   : $symbol - the enum.
1969 #                $declaration - the declaration of the enum.
1970 #############################################################################
1972 sub OutputEnum {
1973     my ($symbol, $declaration) = @_;
1975     my $is_gtype = 0;
1976     if (&CheckIsObject ($symbol)) {
1977         #print "Found enum gtype: $symbol\n";
1978         $is_gtype = 1;
1979     }
1981     my $id;
1982     my $condition;
1983     if ($is_gtype) {
1984         $id = &CreateValidSGMLID ($symbol . "_enum");
1985         $condition = &MakeConditionDescription ($symbol . "_enum");
1986     } else {
1987         $id = &CreateValidSGMLID ($symbol);
1988         $condition = &MakeConditionDescription ($symbol);
1989     }
1991     my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1992     my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1994     $desc .= MakeIndexterms($symbol, $id);
1995     $desc .= "\n";
1996     $desc .= OutputSymbolExtraLinks($symbol);
1997     $desc .= &MakeDeprecationNote($symbol);
1999     if (defined ($SymbolDocs{$symbol})) {
2000         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2001     }
2003     # Create a table of fields and descriptions
2005     my @fields = ParseEnumDeclaration($declaration);
2006     my $params = $SymbolParams{$symbol};
2008     # If nothing at all is documented log a single summary warning at the end.
2009     # Otherwise, warn about each undocumented item.
2011     my $found = 0;
2012     if (defined $params) {
2013         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
2014             if ($params->[$i] =~ /\S/) {
2015                 $found = 1;
2016                 last;
2017             }
2018         }
2019     }
2021     my %field_descrs = (defined $params ? @$params : ());
2022     my $missing_parameters = "";
2023     my $unused_parameters = "";
2025     $desc .= <<EOF;
2026 <refsect3 role="enum_members">\n<title>Members</title>
2027 <informaltable role="enum_members_table" pgwide="1" frame="none">
2028 <tgroup cols="3">
2029 <colspec colname="enum_members_name" colwidth="300px"/>
2030 <colspec colname="enum_members_description"/>
2031 <colspec colname="enum_members_annotations" colwidth="200px"/>
2032 <tbody>
2035     for my $field_name (@fields) {
2036         my $field_descr = $field_descrs{$field_name};
2037         my $param_annotations = "";
2039         $id = &CreateValidSGMLID ($field_name);
2040         $condition = &MakeConditionDescription ($field_name);
2041         $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2042         if (defined $field_descr) {
2043             ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2044             $field_descr = &ConvertMarkDown($symbol, $field_descr);
2045             $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2046             delete $field_descrs{$field_name};
2047         } else {
2048             if ($found) {
2049                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2050                     "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2051                 if ($missing_parameters ne "") {
2052                     $missing_parameters .= ", ".$field_name;
2053                 } else {
2054                     $missing_parameters = $field_name;
2055                 }
2056             }
2057             $desc .= "<entry /><entry />\n";
2058         }
2059         $desc .= "</row>\n";
2060     }
2061     $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2062     foreach my $field_name (keys %field_descrs) {
2063         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2064             "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2065         if ($unused_parameters ne "") {
2066             $unused_parameters .= ", ".$field_name;
2067         } else {
2068             $unused_parameters = $field_name;
2069         }
2070     }
2072     # remember missing/unused parameters (needed in tmpl-free build)
2073     if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2074         $AllIncompleteSymbols{$symbol}=$missing_parameters;
2075     }
2076     if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2077         $AllUnusedSymbols{$symbol}=$unused_parameters;
2078     }
2080     if (!$found) {
2081         if (scalar(@fields) > 0) {
2082             if (! exists ($AllIncompleteSymbols{$symbol})) {
2083                 $AllIncompleteSymbols{$symbol}="<items>";
2084                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2085                     "Value descriptions for $symbol are missing in source code comment block.");
2086             }
2087         }
2088     }
2090     $desc .= OutputSymbolTraits ($symbol);
2091     $desc .= "</refsect2>\n";
2092     return ($synop, $desc);
2096 #############################################################################
2097 # Function    : OutputVariable
2098 # Description : Returns the synopsis and detailed description of a variable.
2099 # Arguments   : $symbol - the extern'ed variable.
2100 #                $declaration - the declaration of the variable.
2101 #############################################################################
2103 sub OutputVariable {
2104     my ($symbol, $declaration) = @_;
2105     my $id = &CreateValidSGMLID ($symbol);
2106     my $condition = &MakeConditionDescription ($symbol);
2107     
2108     @TRACE@("ouputing variable: '$symbol' '$declaration'");
2110     my $type_output;
2111     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*;/) {
2112         my $mod1 = defined ($1) ? $1 : "";
2113         my $ptr = defined ($3) ? $3 : "";
2114         my $space = defined ($4) ? $4 : "";
2115         my $mod2 = defined ($5) ? $5 : "";
2116         $type_output = "extern $mod1$ptr$space$mod2";
2117     } 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*=/) {
2118         my $mod1 = defined ($1) ? $1 : "";
2119         my $ptr = defined ($3) ? $3 : "";
2120         my $space = defined ($4) ? $4 : "";
2121         my $mod2 = defined ($5) ? $5 : "";
2122         $type_output = "$mod1$ptr$space$mod2";
2123     } else {
2124         $type_output = "extern";
2125     }
2126     my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2128     my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2130     $desc .= MakeIndexterms($symbol, $id);
2131     $desc .= "\n";
2132     $desc .= OutputSymbolExtraLinks($symbol);
2134     my $decl_out = &CreateValidSGML ($declaration);
2135     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2137     $desc .= &MakeDeprecationNote($symbol);
2139     if (defined ($SymbolDocs{$symbol})) {
2140         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2141     }
2142     $desc .= OutputSymbolTraits ($symbol);
2143     $desc .= "</refsect2>\n";
2144     return ($synop, $desc);
2148 #############################################################################
2149 # Function    : OutputFunction
2150 # Description : Returns the synopsis and detailed description of a function.
2151 # Arguments   : $symbol - the function.
2152 #                $declaration - the declaration of the function.
2153 #############################################################################
2155 sub OutputFunction {
2156     my ($symbol, $declaration, $symbol_type) = @_;
2157     my $id = &CreateValidSGMLID ($symbol);
2158     my $condition = &MakeConditionDescription ($symbol);
2160     # Take out the return type     $1                                                                                       $2   $3
2161     $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//;
2162     my $type_modifier = defined($1) ? $1 : "";
2163     my $type = $2;
2164     my $pointer = $3;
2165     # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2166     $pointer =~ s/\s+$//;
2167     my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2168     my $start = "";
2169     #if ($symbol_type eq 'USER_FUNCTION') {
2170     #    $start = "typedef ";
2171     #}
2173     # We output const rather than G_CONST_RETURN.
2174     $type_modifier =~ s/G_CONST_RETURN/const/g;
2175     $pointer =~ s/G_CONST_RETURN/const/g;
2176     $pointer =~ s/^\s+/&#160;/g;
2178     my $ret_type_output;
2179     $ret_type_output = "$start$type_modifier$xref$pointer\n";
2181     my $indent_len;
2182     $indent_len = length ($symbol) + 2;
2183     my $char1 = my $char2 = my $char3 = "";
2184     if ($symbol_type eq 'USER_FUNCTION') {
2185         $indent_len += 3;
2186         $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2187         $char2 = "*";
2188         $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2189     }
2191     my ($symbol_output, $symbol_desc_output);
2192     $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2193     if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2194         $symbol_desc_output = "$char1$char2$symbol$char3 ";
2195     } else {
2196         $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2197         $symbol_desc_output = "$char1$char2$symbol$char3\n"
2198           . (' ' x ($indent_len - 1));
2199     }
2201     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";
2203     my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol}&#160;()</title>\n";
2205     $desc .= MakeIndexterms($symbol, $id);
2206     $desc .= "\n";
2207     $desc .= OutputSymbolExtraLinks($symbol);
2209     $desc  .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2211     my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2212                                         sub {
2213                                             &tagify($_[0],"parameter");
2214                                         });
2216     for (my $i = 1; $i <= $#fields; $i += 2) {
2217         my $field_name = $fields[$i];
2219         if ($i == 1) {
2220             $desc  .= "$field_name";
2221         } else {
2222             $desc  .= ",\n"
2223                 . (' ' x $indent_len)
2224                 . "$field_name";
2225         }
2227     }
2229     $desc  .= ");</programlisting>\n";
2231     $desc .= &MakeDeprecationNote($symbol);
2233     my $parameters = &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2234     my $parameters_output = 0;
2236     if (defined ($SymbolDocs{$symbol})) {
2237         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2239         # Try to insert the parameter table at the author's desired position.
2240         # Otherwise we need to tag it onto the end.
2241         # FIXME: document that in the user manual and make it useable for other
2242         # types too
2243         if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
2244           $parameters_output = 1;
2245         }
2246         $desc .= $symbol_docs;
2247     }
2249     if ($parameters_output == 0) {
2250         $desc .= $parameters;
2251     }
2253     $desc .= OutputSymbolTraits ($symbol);
2254     $desc .= "</refsect2>\n";
2255     return ($synop, $desc);
2259 #############################################################################
2260 # Function    : OutputParamDescriptions
2261 # Description : Returns the DocBook output describing the parameters of a
2262 #                function, macro or signal handler.
2263 # Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2264 #                  handlers have an implicit user_data parameter last.
2265 #                $symbol - the name of the function/macro being described.
2266 #               @fields - parsed fields from the declaration, used to determine
2267 #                  undocumented/unused entries
2268 #############################################################################
2270 sub OutputParamDescriptions {
2271     my ($symbol_type, $symbol, @fields) = @_;
2272     my $output = "";
2273     my $params = $SymbolParams{$symbol};
2274     my $num_params = 0;
2275     my %field_descrs = ();
2277     if (@fields) {
2278         %field_descrs = @fields;
2279         delete $field_descrs{"void"};
2280         delete $field_descrs{"Returns"};
2281     }
2283     if (defined $params) {
2284         my $returns = "";
2285         my $params_desc = "";
2286         my $missing_parameters = "";
2287         my $unused_parameters = "";
2288         my $j;
2290         for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2291             my $param_name = $$params[$j];
2292             my $param_desc = $$params[$j + 1];
2293             my $param_annotations = "";
2295             ($param_desc,$param_annotations) = & ExpandAnnotation($symbol, $param_desc);
2296             $param_desc = &ConvertMarkDown($symbol, $param_desc);
2297             # trim
2298             $param_desc =~ s/^(\s|\n)+//msg;
2299             $param_desc =~ s/(\s|\n)+$//msg;
2300             if ($param_name eq "Returns") {
2301                 $returns = "$param_desc\n<para>$param_annotations</para>";
2302             } elsif ($param_name eq "void") {
2303                 #print "!!!! void in params for $symbol?\n";
2304             } else {
2305                 if (@fields) {
2306                     if (!defined $field_descrs{$param_name}) {
2307                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2308                             "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2309                         if ($unused_parameters ne "") {
2310                           $unused_parameters .= ", ".$param_name;
2311                         } else {
2312                            $unused_parameters = $param_name;
2313                         }
2314                     } else {
2315                         delete $field_descrs{$param_name};
2316                     }
2317                 }
2318                 if($param_desc ne "") {
2319                     $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";
2320                     $num_params++;
2321                 }
2322             }
2323         }
2324         foreach my $param_name (keys %field_descrs) {
2325             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2326                 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2327             if ($missing_parameters ne "") {
2328               $missing_parameters .= ", ".$param_name;
2329             } else {
2330                $missing_parameters = $param_name;
2331             }
2332         }
2334         # Signals have an implicit user_data parameter which we describe.
2335         if ($symbol_type eq "SIGNAL") {
2336             $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";
2337         }
2339         # Start a table if we need one.
2340         if ($params_desc ne "") {
2341           $output .= <<EOF;
2342 <refsect3 role="parameters">\n<title>Parameters</title>
2343 <informaltable role="parameters_table" pgwide="1" frame="none">
2344 <tgroup cols="3">
2345 <colspec colname="parameters_name" colwidth="150px"/>
2346 <colspec colname="parameters_description"/>
2347 <colspec colname="parameters_annotations" colwidth="200px"/>
2348 <tbody>
2350           $output .= $params_desc;
2351           $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2352         }
2354         # Output the returns info last
2355         if ($returns ne "") {
2356           $output .= <<EOF;
2357 <refsect3 role=\"returns\">\n<title>Returns</title>
2359           $output .= $returns;
2360           $output .= "\n</refsect3>";
2361         }
2363         # remember missing/unused parameters (needed in tmpl-free build)
2364         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2365             $AllIncompleteSymbols{$symbol}=$missing_parameters;
2366         }
2367         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2368             $AllUnusedSymbols{$symbol}=$unused_parameters;
2369         }
2370     }
2371     if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2372         if (! exists ($AllIncompleteSymbols{$symbol})) {
2373             $AllIncompleteSymbols{$symbol}="<parameters>";
2374         }
2375     }
2377     return $output;
2381 #############################################################################
2382 # Function    : ParseStabilityLevel
2383 # Description : Parses a stability level and outputs a warning if it isn't
2384 #               valid.
2385 # Arguments   : $stability - the stability text.
2386 #                $file, $line - context for error message
2387 #                $message - description of where the level is from, to use in
2388 #               any error message.
2389 # Returns     : The parsed stability level string.
2390 #############################################################################
2392 sub ParseStabilityLevel {
2393     my ($stability, $file, $line, $message) = @_;
2395     $stability =~ s/^\s*//;
2396     $stability =~ s/\s*$//;
2397     if ($stability =~ m/^stable$/i) {
2398         $stability = "Stable";
2399     } elsif ($stability =~ m/^unstable$/i) {
2400         $stability = "Unstable";
2401     } elsif ($stability =~ m/^private$/i) {
2402         $stability = "Private";
2403     } else {
2404         &LogWarning ($file, $line, "$message is $stability.".
2405             "It should be one of these: Stable, Unstable, or Private.");
2406     }
2407     return $stability;
2411 #############################################################################
2412 # Function    : OutputSGMLFile
2413 # Description : Outputs the final DocBook file for one section.
2414 # Arguments   : $file - the name of the file.
2415 #               $title - the title from the $MODULE-sections.txt file, which
2416 #                 will be overridden by the title in the template file.
2417 #               $section_id - the SGML id to use for the toplevel tag.
2418 #               $includes - comma-separates list of include files added at top of
2419 #                 synopsis, with '<' '>' around them (if not already enclosed in "").
2420 #               $functions_synop - reference to the DocBook for the Functions Synopsis part.
2421 #               $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2422 #               $functions_details - reference to the DocBook for the Functions Details part.
2423 #               $other_details - reference to the DocBook for the Types and Values Details part.
2424 #               $signal_synop - reference to the DocBook for the Signal Synopsis part
2425 #               $signal_desc - reference to the DocBook for the Signal Description part
2426 #               $args_synop - reference to the DocBook for the Arg Synopsis part
2427 #               $args_desc - reference to the DocBook for the Arg Description part
2428 #               $hierarchy - reference to the DocBook for the Object Hierarchy part
2429 #               $interfaces - reference to the DocBook for the Interfaces part
2430 #               $implementations - reference to the DocBook for the Known Implementations part
2431 #               $prerequisites - reference to the DocBook for the Prerequisites part
2432 #               $derived - reference to the DocBook for the Derived Interfaces part
2433 #               $file_objects - reference to an array of objects in this file
2434 #############################################################################
2436 sub OutputSGMLFile {
2437     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) = @_;
2439     #print "Output sgml for file $file with title '$title'\n";
2441     # The edited title overrides the one from the sections file.
2442     my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2443     if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2444         $title = $new_title;
2445         #print "Found title: $title\n";
2446     }
2447     my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2448     if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2449         $short_desc = "";
2450     } else {
2451         # Don't use ConvertMarkDown here for now since we don't want blocks
2452         $short_desc = &ExpandAbbreviations("$title:Short_description",
2453                                            $short_desc);
2454         #print "Found short_desc: $short_desc";
2455     }
2456     my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2457     if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2458         $long_desc = "";
2459     } else {
2460         $long_desc = &ConvertMarkDown("$title:Long_description",
2461                                           $long_desc);
2462         #print "Found long_desc: $long_desc";
2463     }
2464     my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2465     if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2466         $see_also = "";
2467     } else {
2468         $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2469         #print "Found see_also: $see_also";
2470     }
2471     if ($see_also) {
2472         $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2473     }
2474     my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2475     if (!defined ($stability) || $stability =~ m/^\s*$/) {
2476         $stability = "";
2477     } else {
2478         $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2479         #print "Found stability: $stability";
2480     }
2481     if ($stability) {
2482         $AnnotationsUsed{$stability} = 1;
2483         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2484     } elsif ($DEFAULT_STABILITY) {
2485         $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2486         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2487     }
2489     my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2490     if (!defined ($image) || $image =~ m/^\s*$/) {
2491       $image = "";
2492     } else {
2493       $image =~ s/^\s*//;
2494       $image =~ s/\s*$//;
2496       my $format;
2498       if ($image =~ /jpe?g$/i) {
2499         $format = "format='JPEG'";
2500       } elsif ($image =~ /png$/i) {
2501         $format = "format='PNG'";
2502       } elsif ($image =~ /svg$/i) {
2503         $format = "format='SVG'";
2504       } else {
2505         $format = "";
2506       }
2508       $image = "  <inlinegraphic fileref='$image' $format/>\n"
2509     }
2511     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2512         gmtime (time);
2513     my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2514     $year += 1900;
2516     my $include_output = "";
2517     if ($includes) {
2518       $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2519       my $include;
2520       foreach $include (split (/,/, $includes)) {
2521         if ($include =~ m/^\".+\"$/) {
2522           $include_output .= "#include ${include}\n";
2523         }
2524         else {
2525           $include =~ s/^\s+|\s+$//gs;
2526           $include_output .= "#include &lt;${include}&gt;\n";
2527         }
2528       }
2529       $include_output .= "</synopsis></refsect1>\n";
2530     }
2532     my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2534     my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT";
2535     my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new";
2537     open (OUTPUT, ">$new_sgml_file")
2538         || die "Can't create $new_sgml_file: $!";
2540     my $object_anchors = "";
2541     foreach my $object (@$file_objects) {
2542         next if ($object eq $section_id);
2543         my $id = CreateValidSGMLID($object);
2544         #print "Debug: Adding anchor for $object\n";
2545         $object_anchors .= "<anchor id=\"$id\"$empty_element_end";
2546     }
2548     # We used to output this, but is messes up our UpdateFileIfChanged code
2549     # since it changes every day (and it is only used in the man pages):
2550     # "<refentry id="$section_id" revision="$mday $month $year">"
2552     if ($OUTPUT_FORMAT eq "xml") {
2553         print OUTPUT $doctype_header;
2554     }
2556     print OUTPUT <<EOF;
2557 <refentry id="$section_id">
2558 <refmeta>
2559 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2560 <manvolnum>3</manvolnum>
2561 <refmiscinfo>
2562   \U$MODULE\E Library
2563 $image</refmiscinfo>
2564 </refmeta>
2565 <refnamediv>
2566 <refname>$title</refname>
2567 <refpurpose>$short_desc</refpurpose>
2568 </refnamediv>
2569 $stability
2570 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2571 $include_output
2572 <refsect1 id="$section_id.description" role="desc">
2573 <title role="desc.title">Description</title>
2574 $extralinks$long_desc
2575 </refsect1>
2576 <refsect1 id="$section_id.functions_details" role="details">
2577 <title role="details.title">Functions</title>
2578 $$functions_details
2579 </refsect1>
2580 <refsect1 id="$section_id.other_details" role="details">
2581 <title role="details.title">Types and Values</title>
2582 $$other_details
2583 </refsect1>
2584 $$args_desc$$signals_desc$see_also
2585 </refentry>
2587     close (OUTPUT);
2589     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2593 #############################################################################
2594 # Function    : OutputExtraFile
2595 # Description : Copies an "extra" DocBook file into the output directory,
2596 #               expanding abbreviations
2597 # Arguments   : $file - the source file.
2598 #############################################################################
2599 sub OutputExtraFile {
2600     my ($file) = @_;
2602     my $basename;
2604     ($basename = $file) =~ s!^.*/!!;
2606     my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename";
2607     my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new";
2609     my $contents;
2611     open(EXTRA_FILE, "<$file") || die "Can't open $file";
2613     {
2614         local $/;
2615         $contents = <EXTRA_FILE>;
2616     }
2618     open (OUTPUT, ">$new_sgml_file")
2619         || die "Can't create $new_sgml_file: $!";
2621     print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2622     close (OUTPUT);
2624     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2626 #############################################################################
2627 # Function    : OutputBook
2628 # Description : Outputs the SGML entities that need to be included into the
2629 #                main SGML file for the module.
2630 # Arguments   : $book_top - the declarations of the entities, which are added
2631 #                  at the top of the main SGML file.
2632 #                $book_bottom - the references to the entities, which are
2633 #                  added in the main SGML file at the desired position.
2634 #############################################################################
2636 sub OutputBook {
2637     my ($book_top, $book_bottom) = @_;
2639     my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top";
2640     my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new";
2642     open (OUTPUT, ">$new_file")
2643         || die "Can't create $new_file: $!";
2644     print OUTPUT $book_top;
2645     close (OUTPUT);
2647     &UpdateFileIfChanged ($old_file, $new_file, 0);
2650     $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom";
2651     $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new";
2653     open (OUTPUT, ">$new_file")
2654         || die "Can't create $new_file: $!";
2655     print OUTPUT $book_bottom;
2656     close (OUTPUT);
2658     &UpdateFileIfChanged ($old_file, $new_file, 0);
2661     # If the main SGML/XML file hasn't been created yet, we create it here.
2662     # The user can tweak it later.
2663     if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2664       open (OUTPUT, ">$MAIN_SGML_FILE")
2665         || die "Can't create $MAIN_SGML_FILE: $!";
2667       if ($OUTPUT_FORMAT eq "xml") {
2668           print OUTPUT <<EOF;
2669 <?xml version="1.0"?>
2670 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2671                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2673   <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
2675 <book id="index">
2677       } else {
2678         print OUTPUT <<EOF;
2679 <!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
2680 $book_top
2682 <book id="index">
2684       }
2686 print OUTPUT <<EOF;
2687   <bookinfo>
2688     <title>$MODULE Reference Manual</title>
2689     <releaseinfo>
2690       for $MODULE [VERSION].
2691       The latest version of this documentation can be found on-line at
2692       <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2693     </releaseinfo>
2694   </bookinfo>
2696   <chapter>
2697     <title>[Insert title here]</title>
2698     $book_bottom
2699   </chapter>
2701   if (-e $OBJECT_TREE_FILE) {
2702     print OUTPUT <<EOF;
2703   <chapter id="object-tree">
2704     <title>Object Hierarchy</title>
2705      <xi:include href="xml/tree_index.sgml"/>
2706   </chapter>
2708   }
2710 print OUTPUT <<EOF;
2711   <index id="api-index-full">
2712     <title>API Index</title>
2713     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2714   </index>
2715   <index id="deprecated-api-index" role="deprecated">
2716     <title>Index of deprecated API</title>
2717     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2718   </index>
2720   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2721 </book>
2724       close (OUTPUT);
2725     }
2729 #############################################################################
2730 # Function    : CreateValidSGML
2731 # Description : This turns any chars which are used in SGML into entities,
2732 #                e.g. '<' into '&lt;'
2733 # Arguments   : $text - the text to turn into proper SGML.
2734 #############################################################################
2736 sub CreateValidSGML {
2737     my ($text) = @_;
2738     $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
2739     $text =~ s/</&lt;/g;
2740     $text =~ s/>/&gt;/g;
2741     # browers render single tabs inconsistently
2742     $text =~ s/([^\s])\t([^\s])/$1&#160;$2/g;
2743     return $text;
2746 #############################################################################
2747 # Function    : ConvertSGMLChars
2748 # Description : This is used for text in source code comment blocks, to turn
2749 #               chars which are used in SGML into entities, e.g. '<' into
2750 #               '&lt;'. Depending on $INLINE_MARKUP_MODE, this is done
2751 #               unconditionally or only if the character doesn't seem to be
2752 #               part of an SGML construct (tag or entity reference).
2753 # Arguments   : $text - the text to turn into proper SGML.
2754 #############################################################################
2756 sub ConvertSGMLChars {
2757     my ($symbol, $text) = @_;
2759     if ($INLINE_MARKUP_MODE) {
2760         # For the XML/SGML mode only convert to entities outside CDATA sections.
2761         return &ModifyXMLElements ($text, $symbol,
2762                                    "<!\\[CDATA\\[|<programlisting[^>]*>",
2763                                    \&ConvertSGMLCharsEndTag,
2764                                    \&ConvertSGMLCharsCallback);
2765     } else {
2766         # For the simple non-sgml mode, convert to entities everywhere.
2767         $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
2768         $text =~ s/</&lt;/g;
2769         $text =~ s/>/&gt;/g;
2770         return $text;
2771     }
2775 sub ConvertSGMLCharsEndTag {
2776   if ($_[0] eq "<!\[CDATA\[") {
2777     return "]]>";
2778   } else {
2779     return "</programlisting>";
2780   }
2783 sub ConvertSGMLCharsCallback {
2784   my ($text, $symbol, $tag) = @_;
2786   if ($tag =~ m/^<programlisting/) {
2787     # We can handle <programlisting> specially here.
2788     return &ModifyXMLElements ($text, $symbol,
2789                                "<!\\[CDATA\\[",
2790                                \&ConvertSGMLCharsEndTag,
2791                                \&ConvertSGMLCharsCallback2);
2792   } elsif ($tag eq "") {
2793     # If we're not in CDATA convert to entities.
2794     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2795     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2796     # Allow ">" at beginning of string for blockquote markdown
2797     $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2799     # Handle "#include <xxxxx>"
2800     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2801   }
2803   return $text;
2806 sub ConvertSGMLCharsCallback2 {
2807   my ($text, $symbol, $tag) = @_;
2809   # If we're not in CDATA convert to entities.
2810   # We could handle <programlisting> differently, though I'm not sure it helps.
2811   if ($tag eq "") {
2812     # replace only if its not a tag
2813     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2814     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2815     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
2817     # Handle "#include <xxxxx>"
2818     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2819   }
2821   return $text;
2824 #############################################################################
2825 # Function    : ExpandAnnotation
2826 # Description : This turns annotations into acronym tags.
2827 # Arguments   : $symbol - the symbol being documented, for error messages.
2828 #                $text - the text to expand.
2829 #############################################################################
2830 sub ExpandAnnotation {
2831     my ($symbol, $param_desc) = @_;
2832     my $param_annotations = "";
2834     # look for annotations at the start of the comment part
2835     if ($param_desc =~ m%^\s*\((.*?)\):%) {
2836         my @annotations;
2837         my $annotation;
2838         $param_desc = $';
2840         @annotations = split(/\)\s*\(/,$1);
2841         foreach $annotation (@annotations) {
2842             # need to search for the longest key-match in %AnnotationDefinition
2843             my $match_length=0;
2844             my $match_annotation="";
2845             my $annotationdef;
2846             foreach $annotationdef (keys %AnnotationDefinition) {
2847                 if ($annotation =~ m/^$annotationdef/) {
2848                     if (length($annotationdef)>$match_length) {
2849                         $match_length=length($annotationdef);
2850                         $match_annotation=$annotationdef;
2851                     }
2852                 }
2853             }
2854             my $annotation_extra = "";
2855             if ($match_annotation ne "") {
2856                 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2857                     $annotation_extra = " $1";
2858                 }
2859                 $AnnotationsUsed{$match_annotation} = 1;
2860                 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2861             }
2862             else {
2863                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2864                     "unknown annotation \"$annotation\" in documentation for $symbol.");
2865                 $param_annotations .= "[$annotation]";
2866             }
2867         }
2868         chomp($param_desc);
2869         $param_desc =~ m/^(.*?)\.*\s*$/s;
2870         $param_desc = "$1. ";
2871     }
2872     if ($param_annotations ne "") {
2873         $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2874     }
2875     return ($param_desc, $param_annotations);
2878 #############################################################################
2879 # Function    : ExpandAbbreviations
2880 # Description : This turns the abbreviations function(), macro(), @param,
2881 #                %constant, and #symbol into appropriate DocBook markup.
2882 #               CDATA sections and <programlisting> parts are skipped.
2883 # Arguments   : $symbol - the symbol being documented, for error messages.
2884 #                $text - the text to expand.
2885 #############################################################################
2887 sub ExpandAbbreviations {
2888   my ($symbol, $text) = @_;
2890   # Note: This is a fallback and normally done in the markdown parser
2892   # Convert "|[" and "]|" into the start and end of program listing examples.
2893   # Support \[<!-- language="C" --> modifiers
2894   $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2895   $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2896   $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2898   # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2899   # as such)
2900   return &ModifyXMLElements ($text, $symbol,
2901                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2902                              \&ExpandAbbreviationsEndTag,
2903                              \&ExpandAbbreviationsCallback);
2907 # Returns the end tag (as a regexp) corresponding to the given start tag.
2908 sub ExpandAbbreviationsEndTag {
2909   my ($start_tag) = @_;
2911   if ($start_tag eq "<!\[CDATA\[") {
2912     return "]]>";
2913   } elsif ($start_tag eq "<!DOCTYPE") {
2914     return ">";
2915   } elsif ($start_tag =~ m/<(\w+)/) {
2916     return "</$1>";
2917   }
2920 # Called inside or outside each CDATA or <programlisting> section.
2921 sub ExpandAbbreviationsCallback {
2922   my ($text, $symbol, $tag) = @_;
2924   if ($tag =~ m/^<programlisting/) {
2925     # Handle any embedded CDATA sections.
2926     return &ModifyXMLElements ($text, $symbol,
2927                                "<!\\[CDATA\\[",
2928                                \&ExpandAbbreviationsEndTag,
2929                                \&ExpandAbbreviationsCallback2);
2930   } elsif ($tag eq "") {
2931     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2933     # We are outside any CDATA or <programlisting> sections, so we expand
2934     # any gtk-doc abbreviations.
2936     # Convert '@param()'
2937     # FIXME: we could make those also links ($symbol.$2), but that would be less
2938     # useful as the link target is a few lines up or down
2939     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2941     # Convert 'function()' or 'macro()'.
2942     # if there is abc_*_def() we don't want to make a link to _def()
2943     # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2944     $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2945     # handle #Object.func()
2946     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2948     # Convert '@param', but not '\@param'.
2949     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2950     $text =~ s/\\\@/\@/g;
2952     # Convert '%constant', but not '\%constant'.
2953     # Also allow negative numbers, e.g. %-1.
2954     $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2955     $text =~ s/\\\%/\%/g;
2957     # Convert '#symbol', but not '\#symbol'.
2958     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2959     $text =~ s/\\#/#/g;
2960   }
2962   return $text;
2965 # This is called inside a <programlisting>
2966 sub ExpandAbbreviationsCallback2 {
2967   my ($text, $symbol, $tag) = @_;
2969   if ($tag eq "") {
2970     # We are inside a <programlisting> but outside any CDATA sections,
2971     # so we expand any gtk-doc abbreviations.
2972     # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2973     #        why not just call it
2974     $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2975   } elsif ($tag eq "<![CDATA[") {
2976     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2977     $text = &ReplaceEntities ($text, $symbol);
2978   }
2980   return $text;
2983 sub MakeHashXRef {
2984     my ($symbol, $tag) = @_;;
2985     my $text = $symbol;
2987     # Check for things like '#include', '#define', and skip them.
2988     if ($PreProcessorDirectives{$symbol}) {
2989       return "#$symbol";
2990     }
2992     # Get rid of special suffixes ('-struct','-enum').
2993     $text =~ s/-struct$//;
2994     $text =~ s/-enum$//;
2996     # If the symbol is in the form "Object::signal", then change the symbol to
2997     # "Object-signal" and use "signal" as the text.
2998     if ($symbol =~ s/::/-/) {
2999       $text = "“$'”";
3000     }
3002     # If the symbol is in the form "Object:property", then change the symbol to
3003     # "Object--property" and use "property" as the text.
3004     if ($symbol =~ s/:/--/) {
3005       $text = "“$'”";
3006     }
3008     if ($tag ne "") {
3009       $text = tagify ($text, $tag);
3010     }
3012     return &MakeXRef($symbol, $text);
3016 #############################################################################
3017 # Function    : ModifyXMLElements
3018 # Description : Looks for given XML element tags within the text, and calls
3019 #               the callback on pieces of text inside & outside those elements.
3020 #               Used for special handling of text inside things like CDATA
3021 #               and <programlisting>.
3022 # Arguments   : $text - the text.
3023 #               $symbol - the symbol currently being documented (only used for
3024 #                      error messages).
3025 #               $start_tag_regexp - the regular expression to match start tags.
3026 #                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3027 #                      CDATA sections or programlisting elements.
3028 #               $end_tag_func - function which is passed the matched start tag
3029 #                      and should return the appropriate end tag string regexp.
3030 #               $callback - callback called with each part of the text. It is
3031 #                      called with a piece of text, the symbol being
3032 #                      documented, and the matched start tag or "" if the text
3033 #                      is outside the XML elements being matched.
3034 #############################################################################
3035 sub ModifyXMLElements {
3036     my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3037     my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3038     my $result = "";
3040     while ($text =~ m/$start_tag_regexp/s) {
3041       $before_tag = $`; # Prematch for last successful match string
3042       $start_tag = $&;  # Last successful match
3043       $text = $';       # Postmatch for last successful match string
3045       $result .= &$callback ($before_tag, $symbol, "");
3046       $result .= $start_tag;
3048       # get the matching end-tag for current tag
3049       $end_tag_regexp = &$end_tag_func ($start_tag);
3051       if ($text =~ m/$end_tag_regexp/s) {
3052         $before_tag = $`;
3053         $end_tag = $&;
3054         $text = $';
3056         $result .= &$callback ($before_tag, $symbol, $start_tag);
3057         $result .= $end_tag;
3058       } else {
3059         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3060             "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3061         # Just assume it is all inside the tag.
3062         $result .= &$callback ($text, $symbol, $start_tag);
3063         $text = "";
3064       }
3065     }
3067     # Handle any remaining text outside the tags.
3068     $result .= &$callback ($text, $symbol, "");
3070     return $result;
3073 sub noop {
3074   return $_[0];
3077 # Adds a tag around some text.
3078 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3079 sub tagify {
3080    my ($text, $elem) = @_;
3081    return "<" . $elem . ">" . $text . "</" . $elem . ">";
3085 #############################################################################
3086 # Function    : MakeXRef
3087 # Description : This returns a cross-reference link to the given symbol.
3088 #                Though it doesn't try to do this for a few standard C types
3089 #                that it        knows won't be in the documentation.
3090 # Arguments   : $symbol - the symbol to try to create a XRef to.
3091 #               $text - text text to put inside the XRef, defaults to $symbol
3092 #############################################################################
3094 sub MakeXRef {
3095     my ($symbol, $text) = ($_[0], $_[1]);
3097     $symbol =~ s/^\s+//;
3098     $symbol =~ s/\s+$//;
3100     if (!defined($text)) {
3101         $text = $symbol;
3103         # Get rid of special suffixes ('-struct','-enum').
3104         $text =~ s/-struct$//;
3105         $text =~ s/-enum$//;
3106     }
3108     if ($symbol =~ m/ /) {
3109         return "$text";
3110     }
3112     #print "Getting type link for $symbol -> $text\n";
3114     my $symbol_id = &CreateValidSGMLID ($symbol);
3115     return "<link linkend=\"$symbol_id\">$text</link>";
3119 #############################################################################
3120 # Function    : MakeIndexterms
3121 # Description : This returns a indexterm elements for the given symbol
3122 # Arguments   : $symbol - the symbol to create indexterms for
3123 #############################################################################
3125 sub MakeIndexterms {
3126   my ($symbol, $id) = @_;
3127   my $terms =  "";
3128   my $sortas = "";
3130   # make the index useful, by ommiting the namespace when sorting
3131   if ($NAME_SPACE ne "") {
3132     if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3133        $sortas=" sortas=\"$1\"";
3134     }
3135   }
3137   if (exists $Deprecated{$symbol}) {
3138       $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3139       $IndexEntriesDeprecated{$symbol}=$id;
3140       $IndexEntriesFull{$symbol}=$id;
3141   }
3142   if (exists $Since{$symbol}) {
3143      my $since = $Since{$symbol};
3144      $since =~ s/^\s+//;
3145      $since =~ s/\s+$//;
3146      if ($since ne "") {
3147          $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3148      }
3149      $IndexEntriesSince{$symbol}=$id;
3150      $IndexEntriesFull{$symbol}=$id;
3151   }
3152   if ($terms eq "") {
3153      $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3154      $IndexEntriesFull{$symbol}=$id;
3155   }
3157   return $terms;
3160 #############################################################################
3161 # Function    : MakeDeprecationNote
3162 # Description : This returns a deprecation warning for the given symbol.
3163 # Arguments   : $symbol - the symbol to try to create a warning for.
3164 #############################################################################
3166 sub MakeDeprecationNote {
3167     my ($symbol) = $_[0];
3168     my $desc = "";
3169     if (exists $Deprecated{$symbol}) {
3170         my $note;
3172         $desc .= "<warning><para><literal>$symbol</literal> ";
3174         $note = $Deprecated{$symbol};
3176         if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3177                 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3178         } else {
3179                 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3180         }
3181         $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3182         $note =~ s/^\s+//;
3183         $note =~ s/\s+$//;
3184         if ($note ne "") {
3185             $note = &ConvertMarkDown($symbol, $note);
3186             $desc .= " " . $note;
3187         }
3188         $desc .= "</warning>\n";
3189     }
3190     return $desc;
3193 #############################################################################
3194 # Function    : MakeConditionDescription
3195 # Description : This returns a sumary of conditions for the given symbol.
3196 # Arguments   : $symbol - the symbol to try to create the sumary.
3197 #############################################################################
3199 sub MakeConditionDescription {
3200     my ($symbol) = $_[0];
3201     my $desc = "";
3203     if (exists $Deprecated{$symbol}) {
3204         if ($desc ne "") {
3205             $desc .= "|";
3206         }
3208         if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3209                 $desc .= "deprecated:$1";
3210         } else {
3211                 $desc .= "deprecated";
3212         }
3213     }
3215     if (exists $Since{$symbol}) {
3216         if ($desc ne "") {
3217             $desc .= "|";
3218         }
3220         if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3221                 $desc .= "since:$1";
3222         } else {
3223                 $desc .= "since";
3224         }
3225     }
3227     if (exists $StabilityLevel{$symbol}) {
3228         if ($desc ne "") {
3229             $desc .= "|";
3230         }
3231         $desc .= "stability:".$StabilityLevel{$symbol};
3232     }
3234     if ($desc ne "") {
3235         my $cond = $desc;
3236         $cond =~ s/\"/&quot;/g;
3237         $desc=" condition=\"".$cond."\"";
3238         #print "condition for '$symbol' = '$desc'\n";
3239     }
3240     return $desc;
3243 #############################################################################
3244 # Function    : GetHierarchy
3245 # Description : Returns the DocBook output describing the ancestors and
3246 #               immediate children of a GObject subclass. It uses the
3247 #               global @Objects and @ObjectLevels arrays to walk the tree.
3249 # Arguments   : $object - the GtkObject subclass.
3250 #               @hierarchy - previous hierarchy
3251 #############################################################################
3253 sub GetHierarchy {
3254     my ($object,$hierarchy_ref) = @_;
3255     my @hierarchy = @{$hierarchy_ref};
3256     
3257     # Find object in the objects array.
3258     my $found = 0;
3259     my @children = ();
3260     my $i;
3261     my $level;
3262     my $j;
3263     for ($i = 0; $i < @Objects; $i++) {
3264         if ($found) {
3265             if ($ObjectLevels[$i] <= $level) {
3266             last;
3267         }
3268             elsif ($ObjectLevels[$i] == $level + 1) {
3269                 push (@children, $Objects[$i]);
3270             }
3271         }
3272         elsif ($Objects[$i] eq $object) {
3273             $found = 1;
3274             $j = $i;
3275             $level = $ObjectLevels[$i];
3276         }
3277     }
3278     if (!$found) {
3279         return @hierarchy;
3280     }
3282     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3283     my @ancestors = ();
3284     push (@ancestors, $object);
3285     #print "Level: $level\n";
3286     while ($level > 1) {
3287         $j--;
3288         if ($ObjectLevels[$j] < $level) {
3289             push (@ancestors, $Objects[$j]);
3290             $level = $ObjectLevels[$j];
3291             #print "Level: $level\n";
3292         }
3293     }
3295     # Output the ancestors, indented and with links.
3296     my $last_index = 0;
3297     $level = 1;
3298     for ($i = $#ancestors; $i >= 0; $i--) {
3299         my $link_text;
3300         # Don't add a link to the current object, i.e. when i == 0.
3301         if ($i > 0) {
3302             my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]);
3303             $link_text = "<link linkend=\"$ancestor_id\">$ancestors[$i]</link>";
3304         } else {
3305             $link_text = "$ancestors[$i]";
3306         }
3307         my $indented_text = ' ' x ($level * 4) . $link_text;
3308         # Check if we already have this object
3309         my $index = -1;
3310         for ($j = 0; $j <= $#hierarchy; $j++) {
3311             if ($hierarchy[$j] eq $indented_text) {
3312                 $index = $j;
3313                 last;
3314             }
3315         }
3316         if ($index == -1) {
3317             # We have a new entry, find insert position in alphabetical order
3318             my $indent = ' ' x ($level * 4);
3319             my $found = 0;
3320             for ($j = $last_index; $j <= $#hierarchy; $j++) {
3321                 if ($hierarchy[$j] !~ m/^${indent}/) {
3322                     $last_index = $j;
3323                     $found = 1;
3324                     last;
3325                 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3326                     my $stripped_text = $hierarchy[$j];
3327                     if ($indented_text !~ m/<link linkend/) {
3328                         $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3329                         $stripped_text =~ s%</link>%%;
3330                     }
3331                     if ($indented_text lt $stripped_text) {
3332                         $last_index = $j;
3333                         $found = 1;
3334                         last;
3335                     } 
3336                 }
3337             }
3338             if (!$found) {
3339               $last_index = 1 + $#hierarchy;
3340             }
3341             splice @hierarchy, $last_index, 0, ($indented_text);
3342             $last_index++;
3343         } else {
3344             # Already have this one, remmeber index as base insert point
3345             $last_index = $index + 1;
3346         }
3347         $level++;
3348     }
3349     # Output the children, indented and with links.
3350     for ($i = 0; $i <= $#children; $i++) {
3351         my $id = &CreateValidSGMLID ($children[$i]);
3352         my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3353         splice @hierarchy, $last_index, 0, ($indented_text);
3354         $last_index++;
3355     }    
3357     return @hierarchy; 
3360 #############################################################################
3361 # Function    : GetInterfaces
3362 # Description : Returns the DocBook output describing the interfaces
3363 #               implemented by a class. It uses the global %Interfaces hash.
3364 # Arguments   : $object - the GtkObject subclass.
3365 #############################################################################
3367 sub GetInterfaces {
3368     my ($object) = @_;
3369     my $text = "";
3370     my $i;
3372     # Find object in the objects array.
3373     if (exists($Interfaces{$object})) {
3374         my @ifaces = split(' ', $Interfaces{$object});
3375         $text = <<EOF;
3376 <para>
3377 $object implements
3379         for ($i = 0; $i <= $#ifaces; $i++) {
3380             my $id = &CreateValidSGMLID ($ifaces[$i]);
3381             $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3382             if ($i < $#ifaces - 1) {
3383                 $text .= ', ';
3384             }
3385             elsif ($i < $#ifaces) {
3386                 $text .= ' and ';
3387             }
3388             else {
3389                 $text .= '.';
3390             }
3391         }
3392         $text .= <<EOF;
3393 </para>
3395     }
3397     return $text;
3400 #############################################################################
3401 # Function    : GetImplementations
3402 # Description : Returns the DocBook output describing the implementations
3403 #               of an interface. It uses the global %Interfaces hash.
3404 # Arguments   : $object - the GtkObject subclass.
3405 #############################################################################
3407 sub GetImplementations {
3408     my ($object) = @_;
3409     my @impls = ();
3410     my $text = "";
3411     my $i;
3412     foreach my $key (keys %Interfaces) {
3413         if ($Interfaces{$key} =~ /\b$object\b/) {
3414             push (@impls, $key);
3415         }
3416     }
3417     if ($#impls >= 0) {
3418         @impls = sort @impls;
3419         $text = <<EOF;
3420 <para>
3421 $object is implemented by
3423         for ($i = 0; $i <= $#impls; $i++) {
3424             my $id = &CreateValidSGMLID ($impls[$i]);
3425             $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3426             if ($i < $#impls - 1) {
3427                 $text .= ', ';
3428             }
3429             elsif ($i < $#impls) {
3430                 $text .= ' and ';
3431             }
3432             else {
3433                 $text .= '.';
3434             }
3435         }
3436         $text .= <<EOF;
3437 </para>
3439     }
3440     return $text;
3444 #############################################################################
3445 # Function    : GetPrerequisites
3446 # Description : Returns the DocBook output describing the prerequisites
3447 #               of an interface. It uses the global %Prerequisites hash.
3448 # Arguments   : $iface - the interface.
3449 #############################################################################
3451 sub GetPrerequisites {
3452     my ($iface) = @_;
3453     my $text = "";
3454     my $i;
3456     if (exists($Prerequisites{$iface})) {
3457         $text = <<EOF;
3458 <para>
3459 $iface requires
3461         my @prereqs = split(' ', $Prerequisites{$iface});
3462         for ($i = 0; $i <= $#prereqs; $i++) {
3463             my $id = &CreateValidSGMLID ($prereqs[$i]);
3464             $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3465             if ($i < $#prereqs - 1) {
3466                 $text .= ', ';
3467             }
3468             elsif ($i < $#prereqs) {
3469                 $text .= ' and ';
3470             }
3471             else {
3472                 $text .= '.';
3473             }
3474         }
3475         $text .= <<EOF;
3476 </para>
3478     }
3479     return $text;
3482 #############################################################################
3483 # Function    : GetDerived
3484 # Description : Returns the DocBook output describing the derived interfaces
3485 #               of an interface. It uses the global %Prerequisites hash.
3486 # Arguments   : $iface - the interface.
3487 #############################################################################
3489 sub GetDerived {
3490     my ($iface) = @_;
3491     my $text = "";
3492     my $i;
3494     my @derived = ();
3495     foreach my $key (keys %Prerequisites) {
3496         if ($Prerequisites{$key} =~ /\b$iface\b/) {
3497             push (@derived, $key);
3498         }
3499     }
3500     if ($#derived >= 0) {
3501         @derived = sort @derived;
3502         $text = <<EOF;
3503 <para>
3504 $iface is required by
3506         for ($i = 0; $i <= $#derived; $i++) {
3507             my $id = &CreateValidSGMLID ($derived[$i]);
3508             $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3509             if ($i < $#derived - 1) {
3510                 $text .= ', ';
3511             }
3512             elsif ($i < $#derived) {
3513                 $text .= ' and ';
3514             }
3515             else {
3516                 $text .= '.';
3517             }
3518         }
3519         $text .= <<EOF;
3520 </para>
3522     }
3523     return $text;
3527 #############################################################################
3528 # Function    : GetSignals
3529 # Description : Returns the synopsis and detailed description DocBook output
3530 #                for the signal handlers of a given GtkObject subclass.
3531 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3532 #############################################################################
3534 sub GetSignals {
3535     my ($object) = @_;
3536     my $synop = "";
3537     my $desc = "";
3539     my $i;
3540     for ($i = 0; $i <= $#SignalObjects; $i++) {
3541         if ($SignalObjects[$i] eq $object) {
3542             #print "Found signal: $SignalNames[$i]\n";
3543             my $name = $SignalNames[$i];
3544             my $symbol = "${object}::${name}";
3545             my $id = &CreateValidSGMLID ("$object-$name");
3547             $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3548             $desc .= MakeIndexterms($symbol, $id);
3549             $desc .= "\n";
3550             $desc .= OutputSymbolExtraLinks($symbol);
3552             $desc .= "<programlisting language=\"C\">";
3554             $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3555             my $type_modifier = defined($1) ? $1 : "";
3556             my $type = $2;
3557             my $pointer = $3;
3558             my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3560             my $ret_type_output = "$type_modifier$xref$pointer";
3561             my $callback_name = "user_function";
3562             $desc  .= "${ret_type_output}\n${callback_name} (";
3564             my $indentation = ' ' x (length($callback_name) + 2);
3565             my $pad = $indentation;
3567             my $sourceparams = $SourceSymbolParams{$symbol};
3568             my @params = split ("\n", $SignalPrototypes[$i]);
3569             my $j;
3570             my $l;
3571             my $type_len = length("gpointer");
3572             my $name_len = length("user_data");
3573             # do two passes, the first one is to calculate padding
3574             for ($l = 0; $l < 2; $l++) {
3575                 for ($j = 0; $j <= $#params; $j++) {
3576                     my $param_name;
3577                     # allow alphanumerics, '_', '[' & ']' in param names
3578                     if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3579                         $type = $1;
3580                         $pointer = $2;
3581                         if (defined($sourceparams)) {
3582                             $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3583                         }
3584                         else {
3585                             $param_name = $3;
3586                         }
3587                         if (!defined($param_name)) {
3588                             $param_name = "arg$j";
3589                         }
3590                         if ($l == 0) {
3591                             if (length($type) + length($pointer) > $type_len) {
3592                                 $type_len = length($type) + length($pointer);
3593                             }
3594                             if (length($param_name) > $name_len) {
3595                                 $name_len = length($param_name);
3596                             }
3597                         }
3598                         else {
3599                             $xref = &MakeXRef ($type, &tagify($type, "type"));
3600                             $pad = ' ' x ($type_len - length($type) - length($pointer));
3601                             $desc .= "$xref$pad $pointer${param_name},\n";
3602                             $desc .= $indentation;
3603                         }
3604                     } else {
3605                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3606                              "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3607                     }
3608                 }
3609             }
3610             $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3611             $pad = ' ' x ($type_len - length("gpointer"));
3612             $desc  .= "$xref$pad user_data)";
3613             $desc  .= "</programlisting>\n";
3615             my $flags = $SignalFlags[$i];
3616             my $flags_string = "";
3618             if (defined ($flags)) {
3619               if ($flags =~ m/f/) {
3620                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3621               }
3622               elsif ($flags =~ m/l/) {
3623                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3624               }
3625               elsif ($flags =~ m/c/) {
3626                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3627                 $flags_string = "Cleanup";
3628               }
3629               if ($flags =~ m/r/) {
3630                 if ($flags_string) { $flags_string .= " / "; }
3631                 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3632               }
3633               if ($flags =~ m/d/) {
3634                 if ($flags_string) { $flags_string .= " / "; }
3635                 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3636               }
3637               if ($flags =~ m/a/) {
3638                 if ($flags_string) { $flags_string .= " / "; }
3639                 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3640               }
3641               if ($flags =~ m/h/) {
3642                 if ($flags_string) { $flags_string .= " / "; }
3643                 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3644               }
3645             }
3647             $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";
3649             my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3650             my $parameters_output = 0;
3652             $AllSymbols{$symbol} = 1;
3653             if (defined ($SymbolDocs{$symbol})) {
3654                 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3656                 # Try to insert the parameter table at the author's desired
3657                 # position. Otherwise we need to tag it onto the end.
3658                 if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
3659                   $parameters_output = 1;
3660                 }
3661                 $desc .= $symbol_docs;
3663                 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3664                     $AllDocumentedSymbols{$symbol} = 1;
3665                 }
3666             }
3667             $desc .= &MakeDeprecationNote($symbol);
3669             if ($parameters_output == 0) {
3670                 $desc .= $parameters;
3671             }
3672             if ($flags_string) {
3673                 $desc  .= "<para>Flags: $flags_string</para>\n";
3674             }
3675             $desc .= OutputSymbolTraits ($symbol);
3676             $desc .= "</refsect2>";
3677         }
3678     }
3679     return ($synop, $desc);
3683 #############################################################################
3684 # Function    : GetArgs
3685 # Description : Returns the synopsis and detailed description DocBook output
3686 #                for the Args of a given GtkObject subclass.
3687 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3688 #############################################################################
3690 sub GetArgs {
3691     my ($object) = @_;
3692     my $synop = "";
3693     my $desc = "";
3694     my $child_synop = "";
3695     my $child_desc = "";
3696     my $style_synop = "";
3697     my $style_desc = "";
3699     my $i;
3700     for ($i = 0; $i <= $#ArgObjects; $i++) {
3701         if ($ArgObjects[$i] eq $object) {
3702             #print "Found arg: $ArgNames[$i]\n";
3703             my $name = $ArgNames[$i];
3704             my $flags = $ArgFlags[$i];
3705             my $flags_string = "";
3706             my $kind = "";
3707             my $id_sep = "";
3709             if ($flags =~ m/c/) {
3710                 $kind = "child property";
3711                 $id_sep = "c-";
3712             }
3713             elsif ($flags =~ m/s/) {
3714                 $kind = "style property";
3715                 $id_sep = "s-";
3716             }
3717             else {
3718                 $kind = "property";
3719             }
3721             # Remember only one colon so we don't clash with signals.
3722             my $symbol = "${object}:${name}";
3723             # use two dashes and ev. an extra separator here for the same reason.
3724             my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3726             my $type = $ArgTypes[$i];
3727             my $type_output;
3728             my $range = $ArgRanges[$i];
3729             my $range_output = CreateValidSGML ($range);
3730             my $default = $ArgDefaults[$i];
3731             my $default_output = CreateValidSGML ($default);
3733             if ($type eq "GtkString") {
3734                 $type = "char&#160;*";
3735             }
3736             if ($type eq "GtkSignal") {
3737                 $type = "GtkSignalFunc, gpointer";
3738                 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3739                     . &MakeXRef ("gpointer");
3740             } elsif ($type =~ m/^(\w+)\*$/) {
3741                 $type_output = &MakeXRef ($1, &tagify($1, "type")) . "&#160;*";
3742             } else {
3743                 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3744             }
3746             if ($flags =~ m/r/) {
3747                 $flags_string = "Read";
3748             }
3749             if ($flags =~ m/w/) {
3750                 if ($flags_string) { $flags_string .= " / "; }
3751                 $flags_string .= "Write";
3752             }
3753             if ($flags =~ m/x/) {
3754                 if ($flags_string) { $flags_string .= " / "; }
3755                 $flags_string .= "Construct";
3756             }
3757             if ($flags =~ m/X/) {
3758                 if ($flags_string) { $flags_string .= " / "; }
3759                 $flags_string .= "Construct Only";
3760             }
3762             $AllSymbols{$symbol} = 1;
3763             my $blurb;
3764             if (defined($SymbolDocs{$symbol}) &&
3765                 !IsEmptyDoc($SymbolDocs{$symbol})) {
3766                 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3767                 #print ".. [$SymbolDocs{$symbol}][$blurb]\n";
3768                 $AllDocumentedSymbols{$symbol} = 1;
3769             }
3770             else {
3771                 if (!($ArgBlurbs[$i] eq "")) {
3772                     $AllDocumentedSymbols{$symbol} = 1;
3773                 } else {
3774                     # FIXME: print a warning?
3775                     #print ".. no description\n";
3776                 }
3777                 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3778             }
3780             my $pad1 = " " x (24 - length ($name));
3782             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";
3783             my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3784             $arg_desc .= MakeIndexterms($symbol, $id);
3785             $arg_desc .= "\n";
3786             $arg_desc .= OutputSymbolExtraLinks($symbol);
3788             $arg_desc .= "<programlisting>  “$name”$pad1 $type_output</programlisting>\n";
3789             $arg_desc .= $blurb;
3790             $arg_desc .= &MakeDeprecationNote($symbol);
3792             if ($flags_string) {
3793               $arg_desc  .= "<para>Flags: $flags_string</para>\n";
3794             }
3795             if ($range ne "") {
3796                 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3797             }
3798             if ($default ne "") {
3799                 $arg_desc .= "<para>Default value: $default_output</para>\n";
3800             }
3801             $arg_desc .= OutputSymbolTraits ($symbol);
3802             $arg_desc .= "</refsect2>\n";
3804             if ($flags =~ m/c/) {
3805                 $child_synop .= $arg_synop;
3806                 $child_desc .= $arg_desc;
3807             }
3808             elsif ($flags =~ m/s/) {
3809                 $style_synop .= $arg_synop;
3810                 $style_desc .= $arg_desc;
3811             }
3812             else {
3813                 $synop .= $arg_synop;
3814                 $desc .= $arg_desc;
3815             }
3816         }
3817     }
3818     return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3822 #############################################################################
3823 # Function    : ReadSourceDocumentation
3824 # Description : This reads in the documentation embedded in comment blocks
3825 #                in the source code (for Gnome).
3827 #                Parameter descriptions override any in the template files.
3828 #                Function descriptions are placed before any description from
3829 #                the template files.
3831 #                It recursively descends the source directory looking for .c
3832 #                files and scans them looking for specially-formatted comment
3833 #                blocks.
3835 # Arguments   : $source_dir - the directory to scan.
3836 #############m###############################################################
3838 sub ReadSourceDocumentation {
3839     my ($source_dir) = @_;
3840     my ($file, $dir, @suffix_list, $suffix);
3842     # prepend entries from @SOURCE_DIR
3843     for my $dir (@SOURCE_DIRS) {
3844         # Check if the filename is in the ignore list.
3845         if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3846             @TRACE@("Skipping source directory: $source_dir");
3847             return;
3848         }
3849     }
3851     @TRACE@("Scanning source directory: $source_dir");
3853     # This array holds any subdirectories found.
3854     my (@subdirs) = ();
3856     @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3858     opendir (SRCDIR, $source_dir)
3859         || die "Can't open source directory $source_dir: $!";
3861     foreach $file (readdir (SRCDIR)) {
3862       if ($file =~ /^\./) {
3863         next;
3864       } elsif (-d "$source_dir/$file") {
3865         push (@subdirs, $file);
3866       } elsif (@suffix_list) {
3867         foreach $suffix (@suffix_list) {
3868           if ($file =~ m/\.\Q${suffix}\E$/) {
3869             &ScanSourceFile ("$source_dir/$file");
3870           }
3871         }
3872       } elsif ($file =~ m/\.[ch]$/) {
3873         &ScanSourceFile ("$source_dir/$file");
3874       }
3875     }
3876     closedir (SRCDIR);
3878     # Now recursively scan the subdirectories.
3879     foreach $dir (@subdirs) {
3880         &ReadSourceDocumentation ("$source_dir/$dir");
3881     }
3885 #############################################################################
3886 # Function    : ScanSourceFile
3887 # Description : Scans one source file looking for specially-formatted comment
3888 #                blocks. Later &MergeSourceDocumentation is used to merge any
3889 #                documentation found with the documentation already read in
3890 #                from the template files.
3892 # Arguments   : $file - the file to scan.
3893 #############################################################################
3895 sub ScanSourceFile {
3896     my ($file) = @_;
3897     my $basename;
3899     # prepend entries from @SOURCE_DIR
3900     for my $dir (@SOURCE_DIRS) {
3901         # Check if the filename is in the ignore list.
3902         if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3903             @TRACE@("Skipping source file: $file");
3904             return;
3905         }
3906     }
3908     if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3909         $basename = $1;
3910     } else {
3911         &LogWarning ($file, 1, "Can't find basename for this filename.");
3912         $basename = $file;
3913     }
3915     # Check if the basename is in the list of files to ignore.
3916     if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3917         @TRACE@("Skipping source file: $file");
3918         return;
3919     }
3921     @TRACE@("Scanning source file: $file");
3923     open (SRCFILE, $file)
3924         || die "Can't open $file: $!";
3925     my $in_comment_block = 0;
3926     my $symbol;
3927     my $in_part = "";
3928     my ($description, $return_desc, $return_start, $return_style);
3929     my ($since_desc, $stability_desc, $deprecated_desc);
3930     my $current_param;
3931     my $ignore_broken_returns;
3932     my @params;
3933     while (<SRCFILE>) {
3934         # Look for the start of a comment block.
3935         if (!$in_comment_block) {
3936             if (m%^\s*/\*.*\*/%) {
3937                 #one-line comment - not gtkdoc
3938             } elsif (m%^\s*/\*\*\s%) {
3939                 #print "Found comment block start\n";
3941                 $in_comment_block = 1;
3943                 # Reset all the symbol data.
3944                 $symbol = "";
3945                 $in_part = "";
3946                 $description = "";
3947                 $return_desc = "";
3948                 $return_style = "";
3949                 $since_desc = "";
3950                 $deprecated_desc = "";
3951                 $stability_desc = "";
3952                 $current_param = -1;
3953                 $ignore_broken_returns = 0;
3954                 @params = ();
3955             }
3956             next;
3957         }
3959         # We're in a comment block. Check if we've found the end of it.
3960         if (m%^\s*\*+/%) {
3961             if (!$symbol) {
3962                 # maybe its not even meant to be a gtk-doc comment?
3963                 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3964             } else {
3965                 # Add the return value description onto the end of the params.
3966                 if ($return_desc) {
3967                     push (@params, "Returns");
3968                     push (@params, $return_desc);
3969                     if ($return_style eq 'broken') {
3970                         &LogWarning ($file, $., "Free-form return value description in $symbol. Use `Returns:' to avoid ambiguities.");
3971                     }
3972                 }
3973                 # Convert special SGML characters
3974                 $description = &ConvertSGMLChars ($symbol, $description);
3975                 my $k;
3976                 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3977                     $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3978                 }
3980                 # Handle Section docs
3981                 if ($symbol =~ m/SECTION:\s*(.*)/) {
3982                     my $real_symbol=$1;
3983                     my $key;
3985                     if (scalar %KnownSymbols) {
3986                         if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
3987                             &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
3988                         }
3989                     }
3991                     #print "SECTION DOCS found in source for : '$real_symbol'\n";
3992                     $ignore_broken_returns = 1;
3993                     for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3994                         #print "   '".$params[$k]."'\n";
3995                         $params[$k] = "\L$params[$k]";
3996                         undef $key;
3997                         if ($params[$k] eq "short_description") {
3998                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
3999                         } elsif ($params[$k] eq "see_also") {
4000                             $key = "$TMPL_DIR/$real_symbol:See_Also";
4001                         } elsif ($params[$k] eq "title") {
4002                             $key = "$TMPL_DIR/$real_symbol:Title";
4003                         } elsif ($params[$k] eq "stability") {
4004                             $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4005                         } elsif ($params[$k] eq "section_id") {
4006                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
4007                         } elsif ($params[$k] eq "include") {
4008                             $key = "$TMPL_DIR/$real_symbol:Include";
4009                         } elsif ($params[$k] eq "image") {
4010                             $key = "$TMPL_DIR/$real_symbol:Image";
4011                         }
4012                         if (defined($key)) {
4013                             $SourceSymbolDocs{$key}=$params[$k+1];
4014                             $SourceSymbolSourceFile{$key} = $file;
4015                             $SourceSymbolSourceLine{$key} = $.;
4016                         }
4017                     }
4018                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4019                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4020                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4021                     #$SourceSymbolTypes{$symbol} = "SECTION";
4022                 } else {
4023                     #print "SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n";
4024                     $SourceSymbolDocs{$symbol} = $description;
4025                     $SourceSymbolParams{$symbol} = [ @params ];
4026                     # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4027                     #if (defined $DeclarationTypes{$symbol}) {
4028                     #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4029                     #}
4030                     $SourceSymbolSourceFile{$symbol} = $file;
4031                     $SourceSymbolSourceLine{$symbol} = $.;
4032                 }
4034                 if ($since_desc) {
4035                      ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4036                      $since_desc =~ s/^\s+//;
4037                      $since_desc =~ s/\s+$//;
4038                      #print "Since($symbol) : [$since_desc]\n";
4039                      $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4040                      if(scalar @extra_lines) {
4041                          &LogWarning ($file, $., "multi-line since docs found");
4042                      }
4043                 }
4045                 if ($stability_desc) {
4046                     $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4047                     $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4048                 }
4050                 if ($deprecated_desc) {
4051                     if (!exists $Deprecated{$symbol}) {
4052                          # don't warn for signals and properties
4053                          #if ($symbol !~ m/::?(.*)/) {
4054                          if (defined $DeclarationTypes{$symbol}) {
4055                              &LogWarning ($file, $.,
4056                                  "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4057                                  " (See the --deprecated-guards option for gtkdoc-scan.)");
4058                          }
4059                     }
4060                     $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4061                 }
4062             }
4064             $in_comment_block = 0;
4065             next;
4066         }
4068         # Get rid of ' * ' at start of every line in the comment block.
4069         s%^\s*\*\s?%%;
4070         # But make sure we don't get rid of the newline at the end.
4071         if (!$_) {
4072             $_ = "\n";
4073         }
4074         #print "DEBUG: scanning :$_";
4076         # If we haven't found the symbol name yet, look for it.
4077         if (!$symbol) {
4078             if (m%^\s*(SECTION:\s*\S+)%) {
4079                 $symbol = $1;
4080                 #print "SECTION DOCS found in source for : '$symbol'\n";
4081                 $ignore_broken_returns = 1;
4082             } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-a-z0-9_ ]+\)\s*)*$%) {
4083                 $symbol = $1;
4084                 #print "SYMBOL DOCS found in source for : '$symbol'\n";
4085             }
4086             next;
4087         }
4089         if ($in_part eq "description") {
4090             # Get rid of 'Description:'
4091             s%^\s*Description:%%;
4092         }
4094         if (m/^\s*(returns:|return\s+value:)/i) {
4095             if ($return_style eq 'broken') {
4096                 $description .= $return_start . $return_desc;
4097             }
4098             $return_start = $1;
4099             if ($return_style eq 'sane') {
4100                 &LogWarning ($file, $., "Multiple Returns for $symbol.");
4101             }
4102             $return_style = 'sane';
4103             $ignore_broken_returns = 1;
4104             $return_desc = $';
4105             $in_part = "return";
4106             next;
4107         } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
4108             $return_start = $1;
4109             $return_style = 'broken';
4110             $return_desc = $';
4111             $in_part = "return";
4112             next;
4113         } elsif (m%^\s*since:%i) {
4114             # we're in param section and have not seen the blank line
4115             if($in_part ne "") {
4116               $since_desc = $';
4117               $in_part = "since";
4118               next;
4119             }
4120         } elsif (m%^\s*deprecated:%i) {
4121             # we're in param section and have not seen the blank line
4122             if($in_part ne "") {
4123               $deprecated_desc = $';
4124               $in_part = "deprecated";
4125               next;
4126             }
4127         } elsif (m%^\s*stability:%i) {
4128             $stability_desc = $';
4129             $in_part = "stability";
4130             next;
4131         }
4133         if ($in_part eq "description") {
4134             $description .= $_;
4135             next;
4136         } elsif ($in_part eq "return") {
4137             $return_desc .= $_;
4138             next;
4139         } elsif ($in_part eq "since") {
4140             $since_desc .= $_;
4141             next;
4142         } elsif ($in_part eq "stability") {
4143             $stability_desc .= $_;
4144             next;
4145         } elsif ($in_part eq "deprecated") {
4146             $deprecated_desc .= $_;
4147             next;
4148         }
4150         # We must be in the parameters. Check for the empty line below them.
4151         if (m%^\s*$%) {
4152             $in_part = "description";
4153             next;
4154         }
4156         # Look for a parameter name.
4157         if (m%^\s*@(\S+)\s*:\s*%) {
4158             my $param_name = $1;
4159             my $param_desc = $';
4161             #print "Found parameter: $param_name\n";
4162             # Allow varargs variations
4163             if ($param_name =~ m/^\.\.\.$/) {
4164                 $param_name = "...";
4165             }
4166             if ("\L$param_name" eq "returns") {
4167                 $return_style = 'sane';
4168                 $ignore_broken_returns = 1;
4169             }
4170             @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4172             push (@params, $param_name);
4173             push (@params, $param_desc);
4174             $current_param += $PARAM_FIELD_COUNT;
4175             next;
4176         }
4178         # We must be in the middle of a parameter description, so add it on
4179         # to the last element in @params.
4180         if ($current_param == -1) {
4181             &LogWarning ($file, $., "Parsing comment block file : parameter expected.");
4182         } else {
4183             $params[$#params] .= $_;
4184         }
4185     }
4186     close (SRCFILE);
4189 #############################################################################
4190 # Function    : OutputMissingDocumentation
4191 # Description : Outputs report of documentation coverage to a file
4193 # Arguments   : none
4194 #############################################################################
4196 sub OutputMissingDocumentation {
4197     my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4198     my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4200     my $n_documented = 0;
4201     my $n_incomplete = 0;
4202     my $total = 0;
4203     my $symbol;
4204     my $percent;
4205     my $msg;
4206     my $buffer = "";
4207     my $buffer_deprecated = "";
4208     my $buffer_descriptions = "";
4210     open(UNDOCUMENTED, ">$new_undocumented_file")
4211       || die "Can't create $new_undocumented_file";
4213     foreach $symbol (sort (keys (%AllSymbols))) {
4214         # FIXME: should we print LogWarnings for undocumented stuff?
4215         # DEBUG
4216         #my $ssfile = &GetSymbolSourceFile($symbol);
4217         #my $ssline = &GetSymbolSourceLine($symbol);
4218         #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4219         # DEBUG
4220         if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4221             $total++;
4222             if (exists ($AllDocumentedSymbols{$symbol})) {
4223                 $n_documented++;
4224                 if (exists ($AllIncompleteSymbols{$symbol})) {
4225                     $n_incomplete++;
4226                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4227                     #$buffer .= "\t0: ".$location;
4228                 }
4229             } elsif (exists $Deprecated{$symbol}) {
4230                 if (exists ($AllIncompleteSymbols{$symbol})) {
4231                     $n_incomplete++;
4232                     $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4233                     #$buffer .= "\t1a: ".$location;
4234                 } else {
4235                     $buffer_deprecated .= $symbol . "\n";
4236                     #$buffer .= "\t1b: ".$location;
4237                 }
4238             } else {
4239                 if (exists ($AllIncompleteSymbols{$symbol})) {
4240                     $n_incomplete++;
4241                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4242                     #$buffer .= "\t2a: ".$location;
4243                 } else {
4244                     $buffer .= $symbol . "\n";
4245                     #$buffer .= "\t2b: ".$location;
4246                 }
4247             }
4248         } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4249             $total++;
4250             #my $len1=(exists($SymbolDocs{$symbol}))?length($SymbolDocs{$symbol}):-1;
4251             #my $len2=(exists($AllDocumentedSymbols{$symbol}))?length($AllDocumentedSymbols{$symbol}):-1;
4252             #print "%%%% $symbol : $len1,$len2\n";
4253             if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4254             || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4255               $n_documented++;
4256             } else {
4257               # cut off the leading namespace ($TMPL_DIR)
4258               $symbol =~ m/^.*\/(.*)$/;
4259               $buffer_descriptions .= $1 . "\n";
4260             }
4261         }
4262     }
4264     $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
4266     if ($total == 0) {
4267       $percent = 100;
4268     } else {
4269       $percent = ($n_documented / $total) * 100.0;
4270     }
4272     printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4273     print UNDOCUMENTED "$n_documented symbols documented.\n";
4274     print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4275     print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
4277     print UNDOCUMENTED $buffer;
4278     close (UNDOCUMENTED);
4280     return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4282     printf "%.0f%% symbol docs coverage", $percent;
4283     print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4284     print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4288 #############################################################################
4289 # Function    : OutputUndeclaredSymbols
4290 # Description : Outputs symbols that are listed in the section file, but not
4291 #               declaration is found in the sources
4293 # Arguments   : none
4294 #############################################################################
4296 sub OutputUndeclaredSymbols {
4297     my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4298     my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4300     open(UNDECLARED, ">$new_undeclared_file")
4301         || die "Can't create $new_undeclared_file";
4303     if (%UndeclaredSymbols) {
4304         print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4305         print UNDECLARED "\n";
4306         print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4307     }
4308     close(UNDECLARED);
4310     return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4313 #############################################################################
4314 # Function    : OutputUnusedSymbols
4315 # Description : Outputs symbols that are documented in comments, but not
4316 #               declared in the sources
4318 # Arguments   : none
4319 #############################################################################
4321 sub OutputUnusedSymbols {
4322     my $num_unused = 0;
4323     my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4324     my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4326     open (UNUSED, ">$new_unused_file")
4327         || die "Can't open $new_unused_file";
4328     my ($symbol);
4329     foreach $symbol (sort keys (%Declarations)) {
4330         if (!defined ($DeclarationOutput{$symbol})) {
4331             print (UNUSED "$symbol\n");
4332             $num_unused++;
4333         }
4334     }
4335     foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4336         print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4337         $num_unused++;
4338     }
4339     close (UNUSED);
4340     if ($num_unused != 0) {
4341         &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4342             "They should be added to $MODULE-sections.txt in the appropriate place.");
4343     }
4345     return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4349 #############################################################################
4350 # Function    : OutputAllSymbols
4351 # Description : Outputs list of all symbols to a file
4353 # Arguments   : none
4354 #############################################################################
4356 sub OutputAllSymbols {
4357      my $n_documented = 0;
4358      my $total = 0;
4359      my $symbol;
4360      my $percent;
4361      my $msg;
4363      open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4364           || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4366      foreach $symbol (sort (keys (%AllSymbols))) {
4367           print SYMBOLS $symbol . "\n";
4368      }
4370      close (SYMBOLS);
4373 #############################################################################
4374 # Function    : OutputSymbolsWithoutSince
4375 # Description : Outputs list of all symbols without a since tag to a file
4377 # Arguments   : none
4378 #############################################################################
4380 sub OutputSymbolsWithoutSince {
4381      my $n_documented = 0;
4382      my $total = 0;
4383      my $symbol;
4384      my $percent;
4385      my $msg;
4387      open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4388           || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4390      foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4391          if (!defined $Since{$symbol}) {
4392              print SYMBOLS $symbol . "\n";
4393          }
4394      }
4396      close (SYMBOLS);
4400 #############################################################################
4401 # Function    : MergeSourceDocumentation
4402 # Description : This merges documentation read from a source file into the
4403 #                documentation read in from a template file.
4405 #                Parameter descriptions override any in the template files.
4406 #                Function descriptions are placed before any description from
4407 #                the template files.
4409 # Arguments   : none
4410 #############################################################################
4412 sub MergeSourceDocumentation {
4413     my $symbol;
4414     my @Symbols;
4416     if (scalar %SymbolDocs) {
4417         @Symbols=keys (%SymbolDocs);
4418         #print "num existing entries: ".(scalar @Symbols)."\n";
4419         #print "  ",$Symbols[0], " ",$Symbols[1],"\n";
4420     }
4421     else {
4422         # filter scanned declarations, with what we suppress from -sections.txt
4423         my %tmp = ();
4424         foreach $symbol (keys (%Declarations)) {
4425             if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4426                 $tmp{$symbol}=1;
4427             }
4428         }
4429         # , add the rest from -sections.txt
4430         foreach $symbol (keys (%KnownSymbols)) {
4431             if ($KnownSymbols{$symbol} == 1) {
4432                 $tmp{$symbol}=1;
4433             }
4434         }
4435         # and add whats found in the source
4436         foreach $symbol (keys (%SourceSymbolDocs)) {
4437             $tmp{$symbol}=1;
4438         }
4439         @Symbols = keys (%tmp);
4440         #print "num source entries: ".(scalar @Symbols)."\n";
4441     }
4442     foreach $symbol (@Symbols) {
4443         $AllSymbols{$symbol} = 1;
4445         my $have_tmpl_docs = 0;
4447         ## see if the symbol is documented in template
4448         my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4449         my $check_tmpl_doc =$tmpl_doc;
4450         # remove all xml-tags and whitespaces
4451         $check_tmpl_doc =~ s/<.*?>//g;
4452         $check_tmpl_doc =~ s/\s//g;
4453         # anything left ?
4454         if ($check_tmpl_doc ne "") {
4455             $have_tmpl_docs = 1;
4456             #print "## [$check_tmpl_doc]\n";
4457         } else {
4458             # if the docs have just an empty para, don't merge that.
4459             $check_tmpl_doc = $tmpl_doc;
4460             $check_tmpl_doc =~ s/(\s|\n)//msg;
4461             if ($check_tmpl_doc eq "<para></para>") {
4462                $tmpl_doc = "";
4463             }
4464         }
4466         if (exists ($SourceSymbolDocs{$symbol})) {
4467             my $type = $DeclarationTypes {$symbol};
4469             #print "merging [$symbol] from source\n";
4471             my $item = "Parameter";
4472             if (defined ($type)) {
4473                 if ($type eq 'STRUCT') {
4474                     $item = "Field";
4475                 } elsif ($type eq 'ENUM') {
4476                     $item = "Value";
4477                 } elsif ($type eq 'UNION') {
4478                     $item = "Field";
4479                 }
4480             } else {
4481                 $type="SIGNAL";
4482             }
4484             my $src_doc = $SourceSymbolDocs{$symbol};
4485             # remove leading and training whitespaces
4486             $src_doc =~ s/^\s+//;
4487             $src_doc =~ s/\s+$//;
4489             # Don't output warnings for overridden titles as titles are
4490             # automatically generated in the -sections.txt file, and thus they
4491             # are often overridden.
4492             if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4493                 # check if content is different
4494                 if ($tmpl_doc ne $src_doc) {
4495                     #print "[$tmpl_doc] [$src_doc]\n";
4496                     &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4497                         "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4498                 }
4499             }
4501             if ($src_doc ne "") {
4502                  $AllDocumentedSymbols{$symbol} = 1;
4503             }
4505             # Convert <!--PARAMETERS--> with any blank lines around it to
4506             # a </para> followed by <!--PARAMETERS--> followed by <para>.
4507             $src_doc =~ s%\n+\s*<!--PARAMETERS-->\s*\n+%\n</para>\n<!--PARAMETERS-->\n<para>\n%g;
4509             # Do not add <para> to nothing, it breaks missing docs checks.
4510             my $src_doc_para = "";
4511             if ($src_doc ne "") {
4512                 $src_doc_para = $src_doc;
4513             }
4515             if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4516                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4517             } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4518                 # For the title/summary/see also section docs we don't want to
4519                 # add any <para> tags.
4520                 $SymbolDocs{$symbol} = "$src_doc"
4521             } else {
4522                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4523             }
4525             # merge parameters
4526             if ($symbol =~ m/.*::.*/) {
4527                 # For signals we prefer the param names from the source docs,
4528                 # since the ones from the templates are likely to contain the
4529                 # artificial argn names which are generated by gtkdoc-scangobj.
4530                 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4531                 # FIXME: we need to check for empty docs here as well!
4532             } else {
4533                 # The templates contain the definitive parameter names and order,
4534                 # so we will not change that. We only override the actual text.
4535                 my $tmpl_params = $SymbolParams{$symbol};
4536                 if (!defined ($tmpl_params)) {
4537                     #print "No merge needed for $symbol\n";
4538                     $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4539                     #  FIXME: we still like to get the number of params and merge
4540                     #  1) we would noticed that params have been removed/renamed
4541                     #  2) we would catch undocumented params
4542                     #  params are not (yet) exported in -decl.txt so that we
4543                     #  could easily grab them :/
4544                 } else {
4545                     my $params = $SourceSymbolParams{$symbol};
4546                     my $j;
4547                     #print "Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n";
4548                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4549                         my $tmpl_param_name = $$tmpl_params[$j];
4551                         # Try to find the param in the source comment documentation.
4552                         my $found = 0;
4553                         my $k;
4554                         #print "  try merge param $tmpl_param_name\n";
4555                         for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4556                             my $param_name = $$params[$k];
4557                             my $param_desc = $$params[$k + 1];
4559                             #print "    test param  $param_name\n";
4560                             # We accept changes in case, since the Gnome source
4561                             # docs contain a lot of these.
4562                             if ("\L$param_name" eq "\L$tmpl_param_name") {
4563                                 $found = 1;
4565                                 # Override the description.
4566                                 $$tmpl_params[$j + 1] = $param_desc;
4568                                 # Set the name to "" to mark it as used.
4569                                 $$params[$k] = "";
4570                                 last;
4571                             }
4572                         }
4574                         # If it looks like the parameters are there, but not
4575                         # in the right place, try to explain a bit better.
4576                         if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4577                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4578                                 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4579                         }
4580                     }
4582                     # Now we output a warning if parameters have been described which
4583                     # do not exist.
4584                     for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4585                         my $param_name = $$params[$j];
4586                         if ($param_name) {
4587                             # the template builder cannot detect if a macro returns
4588                             # a result or not
4589                             if(($type eq "MACRO") && ($param_name eq "Returns")) {
4590                                 # FIXME: do we need to add it then to tmpl_params[] ?
4591                                 my $num=$#$tmpl_params;
4592                                 #print "  adding Returns: to macro docs for $symbol.\n";
4593                                 $$tmpl_params[$num+1]="Returns";
4594                                 $$tmpl_params[$num+2]=$$params[$j+1];
4595                                 next;
4596                             }
4597                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4598                                 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4599                         }
4600                     }
4601                 }
4602             }
4603         } else {
4604             if ($have_tmpl_docs) {
4605                 $AllDocumentedSymbols{$symbol} = 1;
4606                 #print "merging [$symbol] from template\n";
4607             }
4608             else {
4609                 #print "[$symbol] undocumented\n";
4610             }
4611         }
4613         # if this symbol is documented, check if docs are complete
4614         $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4615         # remove all xml-tags and whitespaces
4616         $check_tmpl_doc =~ s/<.*?>//g;
4617         $check_tmpl_doc =~ s/\s//g;
4618         if ($check_tmpl_doc ne "") {
4619             my $tmpl_params = $SymbolParams{$symbol};
4620             if (defined ($tmpl_params)) {
4621                 my $type = $DeclarationTypes {$symbol};
4623                 my $item = "Parameter";
4624                 if (defined ($type)) {
4625                     if ($type eq 'STRUCT') {
4626                         $item = "Field";
4627                     } elsif ($type eq 'ENUM') {
4628                         $item = "Value";
4629                     } elsif ($type eq 'UNION') {
4630                         $item = "Field";
4631                     }
4632                 } else {
4633                     $type="SIGNAL";
4634                 }
4636                 #print "Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n";
4638                 if ($#$tmpl_params > 0) {
4639                     my $j;
4640                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4641                         # Output a warning if the parameter is empty and
4642                         # remember for stats.
4643                         my $tmpl_param_name = $$tmpl_params[$j];
4644                         my $tmpl_param_desc = $$tmpl_params[$j + 1];
4645                         if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4646                             if (exists ($AllIncompleteSymbols{$symbol})) {
4647                                 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4648                             } else {
4649                                 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4650                             }
4651                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4652                                 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4653                         }
4654                     }
4655                 }
4656                 else {
4657                     if ($#$tmpl_params == 0) {
4658                         $AllIncompleteSymbols{$symbol}="<items>";
4659                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4660                             "$item descriptions for $symbol are missing in source code comment block.");
4661                     }
4662                     # $#$tmpl_params==-1 means we don't know about parameters
4663                     # this unfortunately does not tell if there should be some
4664                 }
4665             }
4666         }
4667    }
4668    #print "num doc entries: ".(scalar %SymbolDocs)."\n";
4671 #############################################################################
4672 # Function    : IsEmptyDoc
4673 # Description : Check if a doc-string is empty. Its also regarded as empty if
4674 #               it only consist of whitespace or e.g. FIXME.
4675 # Arguments   : the doc-string
4676 #############################################################################
4677 sub IsEmptyDoc {
4678     my ($doc) = @_;
4680     if ($doc =~ /^\s*$/) {
4681         return 1;
4682     }
4684     if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4685         return 1;
4686     }
4688     return 0;
4691 #############################################################################
4692 # Function    : ConvertMarkDown
4693 # Description : Converts mark down syntax to the respective docbook.
4694 #               http://de.wikipedia.org/wiki/Markdown
4695 #               Inspired by the design of ParseDown
4696 #               http://parsedown.org/
4697 #               Copyright (c) 2013 Emanuil Rusev, erusev.com
4698 # Arguments   : the symbol name, the doc-string
4699 #############################################################################
4701 sub ConvertMarkDown {
4702     my ($symbol, $text) = @_;
4704     $text = &MarkDownParse ($text, $symbol);
4706     return $text
4709 # SUPPORTED MARKDOWN
4710 # ==================
4712 # Atx-style Headers
4713 # -----------------
4715 # # Header 1
4717 # ## Header 2 ##
4719 # Setext-style Headers
4720 # --------------------
4722 # Header 1
4723 # ========
4725 # Header 2
4726 # --------
4728 # Ordered (unnested) Lists
4729 # ------------------------
4731 # 1. item 1
4733 # 1. item 2 with loooong
4734 #    description
4736 # 3. item 3
4738 # Note: we require a blank line above the list items
4741 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4743 sub MarkDownParseBlocks {
4744   my ($linesref, $symbol, $context) = @_;
4745   my $line;
4746   my @md_blocks = ();
4747   my $md_block = { type => "" };
4749  OUTER: foreach $line (@$linesref) {
4750     my $first_char = substr ($line, 0, 1);
4751     my $deindented_line;
4753     if ($md_block->{"type"} eq "markup") {
4754       if (!$md_block->{"closed"}) {
4755         if (index ($line, $md_block->{"start"}) != -1) {
4756           $md_block->{"depth"}++;
4757         }
4758         if (index ($line, $md_block->{"end"}) != -1) {
4759           if ($md_block->{"depth"} > 0) {
4760             $md_block->{"depth"}--;
4761           } else {
4762             $md_block->{"closed"} = 1;
4763           }
4764         }
4765         $md_block->{"text"} .= "\n" . $line;
4766         next OUTER;
4767       }
4768     }
4770     $deindented_line = $line;
4771     $deindented_line =~ s/^\s+//;
4773     if ($md_block->{"type"} eq "heading") {
4774       # a heading is ended by any level less than or equal
4775       if ($md_block->{"level"} == 1) {
4776         if ($line =~ /^={4,}[ \t]*$/) {
4777           my $text = pop @{$md_block->{"lines"}};
4778           $md_block->{"interrupted"} = 0;
4779           push @md_blocks, $md_block;
4781           $md_block = { type => "heading",
4782                         text => $text,
4783                         lines => [],
4784                         level => 1 };
4785           next OUTER;
4786         } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4787           $md_block->{"interrupted"} = 0;
4788           push @md_blocks, $md_block;
4790           $md_block = { type => "heading",
4791                         text => $1,
4792                         id => $2,
4793                         lines => [],
4794                         level => 1 };
4795           next OUTER;
4796         } else {
4797           # push lines into the block until the end is reached
4798           push @{$md_block->{"lines"}}, $line;
4799           next OUTER;
4800         }
4801       } else {
4802         if ($line =~ /^[=]{4,}[ \t]*$/) {
4803           my $text = pop @{$md_block->{"lines"}};
4804           $md_block->{"interrupted"} = 0;
4805           push @md_blocks, $md_block;
4807           $md_block = { type => "heading",
4808                         text => $text,
4809                         lines => [],
4810                         level => 1 };
4811           next OUTER;
4812         } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4813           my $text = pop @{$md_block->{"lines"}};
4814           $md_block->{"interrupted"} = 0;
4815           push @md_blocks, $md_block;
4817           $md_block = { type => "heading",
4818                         text => $text,
4819                         lines => [],
4820                         level => 2 };
4821           next OUTER;
4822         } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4823           $md_block->{"interrupted"} = 0;
4824           push @md_blocks, $md_block;
4826           $md_block = { type => "heading",
4827                         text => $2,
4828                         id => $3,
4829                         lines => [],
4830                         level => length($1) };
4831           next OUTER;
4832         } else {
4833           # push lines into the block until the end is reached
4834           push @{$md_block->{"lines"}}, $line;
4835           next OUTER;
4836         }
4837       }
4838     } elsif ($md_block->{"type"} eq "code") {
4839       if ($line =~ /^[ \t]*\]\|/) {
4840         push @md_blocks, $md_block;
4841         $md_block = { type => "paragraph",
4842                       text => "",
4843                       lines => [] };
4844       } else {
4845         push @{$md_block->{"lines"}}, $line;
4846       }
4847       next OUTER;
4848     }
4850     if ($deindented_line eq "") {
4851       $md_block->{"interrupted"} = 1;
4852       next;
4853     }
4855     if ($md_block->{"type"} eq "quote") {
4856       if (!$md_block->{"interrupted"}) {
4857         $line =~ s/^[ ]*>[ ]?//;
4858         push @{$md_block->{"lines"}}, $line;
4859         next OUTER;
4860       }
4861     } elsif ($md_block->{"type"} eq "li") {
4862       my $marker = $md_block->{"marker"};
4863       if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4864         my $indentation = $1;
4865         if ($md_block->{"indentation"} ne $indentation) {
4866           push @{$md_block->{"lines"}}, $line;
4867         } else {
4868           my $lines = $3;
4869           my $ordered = $md_block->{"ordered"};
4870           $lines =~ s/^[ ]{0,4}//;
4871           $md_block->{"last"} = 0;
4872           push @md_blocks, $md_block;
4873           $md_block = { type => "li",
4874                         ordered => $ordered,
4875                         indentation => $indentation,
4876                         marker => $marker,
4877                         first => 0,
4878                         last => 1,
4879                         lines => [ $lines ] };
4880         }
4881         next OUTER;
4882       }
4884       if ($md_block->{"interrupted"}) {
4885         if ($first_char eq " ") {
4886           push @{$md_block->{"lines"}}, "";
4887           $line =~ s/^[ ]{0,4}//;
4888           push @{$md_block->{"lines"}}, $line;
4889           $md_block->{"interrupted"} = 0;
4890           next OUTER;
4891         }
4892       } else {
4893         $line =~ s/^[ ]{0,4}//;
4894         push @{$md_block->{"lines"}}, $line;
4895         next OUTER;
4896       }
4897     }
4899     # indentation sensitive types
4901     if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4902       # atx heading (#)
4903       push @md_blocks, $md_block;
4905       $md_block = { type => "heading",
4906                     text => $2,
4907                     id => $3,
4908                     lines => [],
4909                     level => length($1) };
4911       next OUTER;
4912     } elsif ($line =~ /^={4,}[ \t]*$/) {
4913       # setext heading (====)
4915       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4916         push @md_blocks, $md_block;
4917         $md_block->{"type"} = "heading";
4918         $md_block->{"lines"} = [];
4919         $md_block->{"level"} = 1;
4920       }
4922       next OUTER;
4923     } elsif ($line =~ /^-{4,}[ \t]*$/) {
4924       # setext heading (-----)
4926       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4927         push @md_blocks, $md_block;
4928         $md_block->{"type"} = "heading";
4929         $md_block->{"lines"} = [];
4930         $md_block->{"level"} = 2;
4931       }
4933       next OUTER;
4934     } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4935       # code
4936       $md_block->{"interrupted"} = 1;
4937       push @md_blocks, $md_block;
4938       $md_block = { type => "code",
4939                     language => $1,
4940                     lines => [] };
4941       next OUTER;
4942     }
4944     # indentation insensitive types
4946     if ($line =~ /^[ ]*<!DOCTYPE/) {
4947       push @md_blocks, $md_block;
4949       $md_block = { type   => "markup",
4950                     text   => $deindented_line,
4951                     start  => "<",
4952                     end    => ">",
4953                     closed => 0,
4954                     depth  => 0 };
4956     } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4957       # markup, including <?xml version="1.0"?>
4958       my $tag = $1;
4959       my $is_self_closing = defined($2);
4960       # FIXME: why do we need to skip https? here, if we generalize this to all
4961       # uri schemes we get parsing errors
4962       if (! $MD_TEXT_LEVEL_ELEMENTS{$tag} && $tag !~ /^https?/) {
4963         push @md_blocks, $md_block;
4965         if ($is_self_closing) {
4966           $md_block = { type => "self-closing tag",
4967                         text => $deindented_line };
4968           $is_self_closing = 0;
4969           next OUTER;
4970         }
4972         $md_block = { type   => "markup",
4973                       text   => $deindented_line,
4974                       start  => "<" . $tag . ">",
4975                       end    => "</" . $tag . ">",
4976                       closed => 0,
4977                       depth  => 0 };
4978         if ($deindented_line =~ /<\/$tag>/) {
4979           $md_block->{"closed"} = 1;
4980         }
4981         next OUTER;
4982       }
4983     } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
4984       # li
4985       push @md_blocks, $md_block;
4986       my $lines = $2;
4987       my $indentation = $1;
4988       $lines =~ s/^[ ]{0,4}//;
4989       $md_block = { type => "li",
4990                     ordered => 0,
4991                     indentation => $indentation,
4992                     marker => "[*+-]",
4993                     first => 1,
4994                     last => 1,
4995                     lines => [ $lines ] };
4996       next OUTER;
4997     } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
4998       push @md_blocks, $md_block;
4999       $md_block = { type => "quote",
5000                     lines => [ $1 ] };
5001       next OUTER;
5002     }
5004     # list item
5006     if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5007       push @md_blocks, $md_block;
5008       my $lines = $2;
5009       my $indentation = $1;
5010       $lines =~ s/^[ ]{0,4}//;
5012       $md_block = { type => "li",
5013                     ordered => 1,
5014                     indentation => $indentation,
5015                     marker => "\\d+[.]",
5016                     first => 1,
5017                     last => 1,
5018                     lines => [ $lines ] };
5020       next;
5021     }
5023     # paragraph
5024     if ($md_block->{"type"} eq "paragraph") {
5025       if ($md_block->{"interrupted"}) {
5026         push @md_blocks, $md_block;
5027         $md_block = { type => "paragraph",
5028                       interrupted => 0,
5029                       text => $line };
5030       } else {
5031         $md_block->{"text"} .= "\n" . $line;
5032       }
5033     } else {
5034       push @md_blocks, $md_block;
5035       $md_block = { type => "paragraph",
5036                     text => $line };
5037     }
5038   }
5040   push @md_blocks, $md_block;
5042   shift @md_blocks;
5044   return @md_blocks;
5047 sub MarkDownParseSpanElementsInner {
5048   my ($text, $markersref) = @_;
5049   my $markup = "";
5050   my %markers = map { $_ => 1 } @$markersref;
5052   while ($text ne "") {
5053     my $closest_marker = "";
5054     my $closest_marker_index = 0;
5055     my $closest_marker_position = -1;
5056     my $text_marker = "";
5057     my $i = 0;
5058     my $offset = 0;
5059     my @markers_rest;
5060     my $marker;
5061     my $use;
5063     while ( ($marker, $use) = each %markers ) {
5064       my $marker_position;
5066       if (!$use) {
5067         next;
5068       }
5070       $marker_position = index ($text, $marker);
5072       if ($marker_position < 0) {
5073         $markers{$marker} = 0;
5074         next;
5075       }
5077       if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5078         $closest_marker = $marker;
5079         $closest_marker_index = $i;
5080         $closest_marker_position = $marker_position;
5081       }
5082     }
5084     if ($closest_marker_position >= 0) {
5085       $text_marker = substr ($text, $closest_marker_position);
5086     }
5088     if ($text_marker eq "") {
5089       $markup .= $text;
5090       $text = "";
5091       next; # last
5092     }
5094     $markup .= substr ($text, 0, $closest_marker_position);
5095     $text = substr ($text, $closest_marker_position);
5096     @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5098     if ($closest_marker eq "![" || $closest_marker eq "[") {
5099       my %element;
5101       if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5102         my $remaining_text;
5104         %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5105                      "a" => $1 );
5107         $offset = length ($&);
5108         if ($element{"!"}) {
5109           $offset++;
5110         }
5112         $remaining_text = substr ($text, $offset);
5113         if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5114           $element{"»"} = $1;
5115           if (defined ($2)) {
5116             $element{"#"} = $2;
5117           }
5118           $offset += length ($&);
5119         } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5120           $element{"ref"} = $1;
5121           $offset += length ($&);
5122         } else {
5123           undef %element;
5124         }
5125       }
5127       if (%element) {
5128         if ($element{"»"}) {
5129           $element{"»"} =~ s/&/&amp;/g;
5130           $element{"»"} =~ s/</&lt;/g;
5131         }
5132         if ($element{"!"}) {
5133           $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5135           if (defined ($element{"a"})) {
5136             $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5137           }
5139           $markup .= "</inlinemediaobject>";
5140         } elsif ($element{"ref"}) {
5141           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5142           $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5144           if (defined ($element{"#"})) {
5145             # title attribute not supported
5146           }
5148           $markup .= ">" . $element{"a"} . "</link>";
5149         } else {
5150           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5151           $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5153           if (defined ($element{"#"})) {
5154             # title attribute not supported
5155           }
5157           $markup .= ">" . $element{"a"} . "</ulink>";
5158         }
5159       } else {
5160         $markup .= $closest_marker;
5161         if ($closest_marker eq "![") {
5162           $offset = 2;
5163         } else {
5164           $offset = 1;
5165         }
5166       }
5167     } elsif ($closest_marker eq "<") {
5168       if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5169         my $element_url = $1;
5170         $element_url =~ s/&/&amp;/g;
5171         $element_url =~ s/</&lt;/g;
5173         $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5174         $offset = length ($&);
5175       } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5176         $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5177         $offset = length ($&);
5178       } elsif ($text =~ /^<[^>]+?>/) {
5179         $markup .= $&;
5180         $offset = length ($&);
5181       } else {
5182         $markup .= "&lt;";
5183         $offset = 1;
5184       }
5185     } elsif ($closest_marker eq "\\") {
5186       my $special_char = substr ($text, 1, 1);
5187       if ($MD_ESCAPABLE_CHARS{$special_char} ||
5188           $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5189         $markup .= $special_char;
5190         $offset = 2;
5191       } else {
5192         $markup .= "\\";
5193         $offset = 1;
5194       }
5195     } elsif ($closest_marker eq "`") {
5196       if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5197         my $element_text = $2;
5198         $markup .= "<literal>" . $element_text . "</literal>";
5199         $offset = length ($&);
5200       } else {
5201         $markup .= "`";
5202         $offset = 1;
5203       }
5204     } elsif ($closest_marker eq "@") {
5205       # Convert '@param()'
5206       # FIXME: we could make those also links ($symbol.$2), but that would be less
5207       # useful as the link target is a few lines up or down
5208       if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5209         $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5210         $offset = length ($&);
5211       } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5212         # Convert '@param', but not '\@param'.
5213         $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5214         $offset = length ($&);
5215       } elsif ($text =~ /^\\\@/) {
5216         $markup .= "\@";
5217         $offset = length ($&);
5218       } else {
5219         $markup .= "@";
5220         $offset = 1;
5221       }
5222     } elsif ($closest_marker eq "#") {
5223       if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5224         # handle #Object.func()
5225         $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5226         $offset = length ($&);
5227       } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5228         # Convert '#symbol', but not '\#symbol'.
5229         $markup .= $1 . &MakeHashXRef ($2, "type");
5230         $offset = length ($&);
5231       } elsif ($text =~ /^\\#/) {
5232         $markup .= "#";
5233         $offset = length ($&);
5234       } else {
5235         $markup .= "#";
5236         $offset = 1;
5237       }
5238     } elsif ($closest_marker eq "%") {
5239       if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5240         # Convert '%constant', but not '\%constant'.
5241         # Also allow negative numbers, e.g. %-1.
5242         $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5243         $offset = length ($&);
5244       } elsif ($text =~ /^\\%/) {
5245         $markup .= "\%";
5246         $offset = length ($&);
5247       } else {
5248         $markup .= "%";
5249         $offset = 1;
5250       }
5251     }
5253     if ($offset > 0) {
5254       $text = substr ($text, $offset);
5255     }
5256   }
5258   return $markup;
5261 sub MarkDownParseSpanElements {
5262   my ($text) = @_;
5263   my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5265   $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5267   # Convert 'function()' or 'macro()'.
5268   # if there is abc_*_def() we don't want to make a link to _def()
5269   # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5270   $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5272   return $text;
5275 sub ReplaceEntities {
5276   my ($text, $symbol) = @_;
5277   my $warn = "";
5278   my @entities = ( [ "&lt;", "<" ],
5279                    [ "&gt;", ">" ],
5280                    [ "&ast;", "*" ],
5281                    [ "&num;", "#" ],
5282                    [ "&percnt;", "%"],
5283                    [ "&colon;", ":" ],
5284                    [ "&quot;", "\"" ],
5285                    [ "&apos;", "'" ],
5286                    [ "&nbsp;", " " ],
5287                    [ "&amp;", "&" ] ); # Do this last, or the others get messed up.
5288   my $i;
5290   # Expand entities in <programlisting> even inside CDATA since
5291   # we changed the definition of |[ to add CDATA
5292   for ($i = 0; $i <= $#entities; $i++) {
5293     if ($text =~ s/$entities[$i][0]/$entities[$i][1]/g) {
5294       # don't warn about &ast; since it is expected to be present
5295       # for C-style comments
5296       if ($entities[$i][0] ne "&ast;") {
5297         $warn .= "$entities[$i][0] ";
5298       }
5299     }
5300   }
5302   if ($warn ne "") {
5303     chomp $warn;
5304     &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
5305                  "Deprecated entities found in documentation for $symbol: $warn");
5306   }
5308   return $text;
5311 sub MarkDownOutputDocBook {
5312   my ($blocksref, $symbol, $context) = @_;
5313   my $output = "";
5314   my $block;
5315   my @blocks = @$blocksref;
5317   foreach $block (@blocks) {
5318     my $text;
5319     my $title;
5321     if ($block->{"type"} eq "paragraph") {
5322       $text = &MarkDownParseSpanElements ($block->{"text"});
5323       if ($context eq "li" && $output eq "") {
5324         if ($block->{"interrupted"}) {
5325           $output .= "\n"."<para>".$text."</para>"."\n";
5326         } else {
5327           $output .= "<para>".$text."</para>";
5328           if ($#blocks > 0) {
5329             $output .= "\n";
5330           }
5331         }
5332       } else {
5333         $output .= "<para>".$text."</para>"."\n";
5334       }
5336     } elsif ($block->{"type"} eq "heading") {
5337       my $tag;
5339       $title = &MarkDownParseSpanElements ($block->{"text"});
5341       if ($block->{"level"} == 1) {
5342         $tag = "refsect2";
5343       } else {
5344         $tag = "refsect3";
5345       }
5347       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5348       if (defined ($block->{"id"})) {
5349         $output .= "<" . $tag . " id=\"" . $block->{"id"} . "\">";
5350       } else {
5351         $output .= "<" . $tag . ">";
5352       }
5354       $output .= "<title>" . $title . "</title>" . $text . "</" . $tag . ">\n";
5355     } elsif ($block->{"type"} eq "li") {
5356       my $tag = "itemizedlist";
5358       if ($block->{"first"}) {
5359         if ($block->{"ordered"}) {
5360           $tag = "orderedlist";
5361         }
5362         $output .= "<".$tag.">\n";
5363       }
5365       if ($block->{"interrupted"}) {
5366         push @{$block->{"lines"}}, "";
5367       }
5369       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5370       $output .= "<listitem>".$text."</listitem>\n";
5371       if ($block->{"last"}) {
5372         if ($block->{"ordered"}) {
5373           $tag = "orderedlist";
5374         }
5375         $output .= "</".$tag.">\n";
5376       }
5377     } elsif ($block->{"type"} eq "quote") {
5378       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5379       $output .= "<blockquote>\n" . $text . "</blockquote>\n";
5380     } elsif ($block->{"type"} eq "code") {
5381       if ($block->{"language"}) {
5382         $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5383       } else {
5384         $output .= "<informalexample><programlisting><![CDATA[\n";
5385       }
5386       foreach (@{$block->{"lines"}}) {
5387         $output .= &ReplaceEntities ($_, $symbol) . "\n";
5388       }
5389       $output .= "]]></programlisting></informalexample>\n";
5390     } elsif ($block->{"type"} eq "markup") {
5391       $text = &ExpandAbbreviations($symbol, $block->{"text"});
5392       $output .= $text."\n";
5393     } else {
5394       $output .= $block->{"text"}."\n";
5395     }
5396   }
5398   return $output;
5401 sub MarkDownParseLines {
5402   my ($linesref, $symbol, $context) = @_;
5403   my $output;
5404   my @lines = @$linesref;
5405   my @blocks;
5407   @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5408   $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5410   return $output;
5413 sub MarkDownParse {
5414   my ($text, $symbol) = @_;
5415   my @lines;
5417   # take out some variability in line endings
5418   $text =~ s%\r\n%\n%g;
5419   $text =~ s%\r%\n%g;
5421   # split lines
5422   @lines = split("\n", $text);
5423   $text = MarkDownParseLines(\@lines, $symbol, "");
5425   return $text;
5428 #############################################################################
5429 # LIBRARY FUNCTIONS -        These functions are used in both gtkdoc-mkdb and
5430 #                        gtkdoc-mktmpl and should eventually be moved to a
5431 #                        separate library.
5432 #############################################################################
5434 #############################################################################
5435 # Function    : ReadDeclarationsFile
5436 # Description : This reads in a file containing the function/macro/enum etc.
5437 #                declarations.
5439 #                Note that in some cases there are several declarations with
5440 #                the same name, e.g. for conditional macros. In this case we
5441 #                set a flag in the %DeclarationConditional hash so the
5442 #                declaration is not shown in the docs.
5444 #                If a macro and a function have the same name, e.g. for
5445 #                gtk_object_ref, the function declaration takes precedence.
5447 #                Some opaque structs are just declared with 'typedef struct
5448 #                _name name;' in which case the declaration may be empty.
5449 #                The structure may have been found later in the header, so
5450 #                that overrides the empty declaration.
5452 # Arguments   : $file - the declarations file to read
5453 #                $override - if declarations in this file should override
5454 #                        any current declaration.
5455 #############################################################################
5457 sub ReadDeclarationsFile {
5458     my ($file, $override) = @_;
5460     if ($override == 0) {
5461         %Declarations = ();
5462         %DeclarationTypes = ();
5463         %DeclarationConditional = ();
5464         %DeclarationOutput = ();
5465     }
5467     open (INPUT, $file)
5468         || die "Can't open $file: $!";
5469     my $declaration_type = "";
5470     my $declaration_name;
5471     my $declaration;
5472     my $is_deprecated = 0;
5473     while (<INPUT>) {
5474         if (!$declaration_type) {
5475             if (m/^<([^>]+)>/) {
5476                 $declaration_type = $1;
5477                 $declaration_name = "";
5478                 #print "Found declaration: $declaration_type\n";
5479                 $declaration = "";
5480             }
5481         } else {
5482             if (m%^<NAME>(.*)</NAME>%) {
5483                 $declaration_name = $1;
5484             } elsif (m%^<DEPRECATED/>%) {
5485                 $is_deprecated = 1;
5486             } elsif (m%^</$declaration_type>%) {
5487                 #print "Found end of declaration: $declaration_name\n";
5488                 # Check that the declaration has a name
5489                 if ($declaration_name eq "") {
5490                     print "ERROR: $declaration_type has no name $file:$.\n";
5491                 }
5493                 # If the declaration is an empty typedef struct _XXX XXX
5494                 # set the flag to indicate the struct has a typedef.
5495                 if ($declaration_type eq 'STRUCT'
5496                     && $declaration =~ m/^\s*$/) {
5497                     #print "Struct has typedef: $declaration_name\n";
5498                     $StructHasTypedef{$declaration_name} = 1;
5499                 }
5501                 # Check if the symbol is already defined.
5502                 if (defined ($Declarations{$declaration_name})
5503                     && $override == 0) {
5504                     # Function declarations take precedence.
5505                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5506                         # Ignore it.
5507                     } elsif ($declaration_type eq 'FUNCTION') {
5508                         if ($is_deprecated) {
5509                             $Deprecated{$declaration_name} = "";
5510                         }
5511                         $Declarations{$declaration_name} = $declaration;
5512                         $DeclarationTypes{$declaration_name} = $declaration_type;
5513                     } elsif ($DeclarationTypes{$declaration_name}
5514                               eq $declaration_type) {
5515                         # If the existing declaration is empty, or is just a
5516                         # forward declaration of a struct, override it.
5517                         if ($declaration_type eq 'STRUCT') {
5518                             if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5519                                 if ($is_deprecated) {
5520                                     $Deprecated{$declaration_name} = "";
5521                                 }
5522                                 $Declarations{$declaration_name} = $declaration;
5523                             } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5524                                 # Ignore an empty or forward declaration.
5525                             } else {
5526                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5527                             }
5528                         } else {
5529                             # set flag in %DeclarationConditional hash for
5530                             # multiply defined macros/typedefs.
5531                             $DeclarationConditional{$declaration_name} = 1;
5532                         }
5533                     } else {
5534                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5535                     }
5536                 } else {
5537                     if ($is_deprecated) {
5538                         $Deprecated{$declaration_name} = "";
5539                     }
5540                     $Declarations{$declaration_name} = $declaration;
5541                     $DeclarationTypes{$declaration_name} = $declaration_type;
5542                 }
5544                 $declaration_type = "";
5545                 $is_deprecated = 0;
5546             } else {
5547                 $declaration .= $_;
5548             }
5549         }
5550     }
5551     close (INPUT);
5555 #############################################################################
5556 # Function    : ReadSignalsFile
5557 # Description : This reads in an existing file which contains information on
5558 #                all GTK signals. It creates the arrays @SignalNames and
5559 #                @SignalPrototypes containing info on the signals. The first
5560 #                line of the SignalPrototype is the return type of the signal
5561 #                handler. The remaining lines are the parameters passed to it.
5562 #                The last parameter, "gpointer user_data" is always the same
5563 #                so is not included.
5564 # Arguments   : $file - the file containing the signal handler prototype
5565 #                        information.
5566 #############################################################################
5568 sub ReadSignalsFile {
5569     my ($file) = @_;
5571     my $in_signal = 0;
5572     my $signal_object;
5573     my $signal_name;
5574     my $signal_returns;
5575     my $signal_flags;
5576     my $signal_prototype;
5578     # Reset the signal info.
5579     @SignalObjects = ();
5580     @SignalNames = ();
5581     @SignalReturns = ();
5582     @SignalFlags = ();
5583     @SignalPrototypes = ();
5585     if (! -f $file) {
5586         return;
5587     }
5588     if (!open (INPUT, $file)) {
5589         warn "Can't open $file - skipping signals\n";
5590         return;
5591     }
5592     while (<INPUT>) {
5593         if (!$in_signal) {
5594             if (m/^<SIGNAL>/) {
5595                 $in_signal = 1;
5596                 $signal_object = "";
5597                 $signal_name = "";
5598                 $signal_returns = "";
5599                 $signal_prototype = "";
5600             }
5601         } else {
5602             if (m/^<NAME>(.*)<\/NAME>/) {
5603                 $signal_name = $1;
5604                 if ($signal_name =~ m/^(.*)::(.*)$/) {
5605                     $signal_object = $1;
5606                     ($signal_name = $2) =~ s/_/-/g;
5607                     #print "Found signal: $signal_name\n";
5608                 } else {
5609                     &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5610                 }
5611             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5612                 $signal_returns = $1;
5613             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5614                 $signal_flags = $1;
5615             } elsif (m%^</SIGNAL>%) {
5616                 #print "Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}";
5617                 push (@SignalObjects, $signal_object);
5618                 push (@SignalNames, $signal_name);
5619                 push (@SignalReturns, $signal_returns);
5620                 push (@SignalFlags, $signal_flags);
5621                 push (@SignalPrototypes, $signal_prototype);
5622                 $in_signal = 0;
5623             } else {
5624                 $signal_prototype .= $_;
5625             }
5626         }
5627     }
5628     close (INPUT);
5632 #############################################################################
5633 # Function    : ReadTemplateFile
5634 # Description : This reads in the manually-edited documentation file
5635 #               corresponding to the file currently being created, so we can
5636 #               insert the documentation at the appropriate places.
5637 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5638 #               is a hash of arrays.
5639 #               NOTE: This function is duplicated in gtkdoc-mktmpl (but
5640 #               slightly different).
5641 # Arguments   : $docsfile - the template file to read in.
5642 #               $skip_unused_params - 1 if the unused parameters should be
5643 #                 skipped.
5644 #############################################################################
5646 sub ReadTemplateFile {
5647     my ($docsfile, $skip_unused_params) = @_;
5649     my $template = "$docsfile.sgml";
5650     if (! -f $template) {
5651         #print "File doesn't exist: $template\n";
5652         return 0;
5653     }
5655     # start with empty hashes, we merge the source comment for each file
5656     # afterwards
5657     %SymbolDocs = ();
5658     %SymbolTypes = ();
5659     %SymbolParams = ();
5661     my $current_type = "";        # Type of symbol being read.
5662     my $current_symbol = "";        # Name of symbol being read.
5663     my $symbol_doc = "";                # Description of symbol being read.
5664     my @params;                        # Parameter names and descriptions of current
5665                                 #   function/macro/function typedef.
5666     my $current_param = -1;        # Index of parameter currently being read.
5667                                 #   Note that the param array contains pairs
5668                                 #   of param name & description.
5669     my $in_unused_params = 0;        # True if we are reading in the unused params.
5670     my $in_deprecated = 0;
5671     my $in_since = 0;
5672     my $in_stability = 0;
5674     open (DOCS, "$template")
5675         || die "Can't open $template: $!";
5677     @TRACE@("reading template $template");
5679     while (<DOCS>) {
5680         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5681             my $type = $1;
5682             my $symbol = $2;
5683             if ($symbol eq "Title"
5684                 || $symbol eq "Short_Description"
5685                 || $symbol eq "Long_Description"
5686                 || $symbol eq "See_Also"
5687                 || $symbol eq "Stability_Level"
5688                 || $symbol eq "Include"
5689                 || $symbol eq "Image") {
5691                 $symbol = $docsfile . ":" . $symbol;
5692             }
5694             #print "Found symbol: $symbol\n";
5695             # Remember file and line for the symbol
5696             $SymbolSourceFile{$symbol} = $template;
5697             $SymbolSourceLine{$symbol} = $.;
5699             # Store previous symbol, but remove any trailing blank lines.
5700             if ($current_symbol ne "") {
5701                 $symbol_doc =~ s/\s+$//;
5702                 $SymbolTypes{$current_symbol} = $current_type;
5703                 $SymbolDocs{$current_symbol} = $symbol_doc;
5705                 # Check that the stability level is valid.
5706                 if ($StabilityLevel{$current_symbol}) {
5707                     $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5708                 }
5710                 if ($current_param >= 0) {
5711                     $SymbolParams{$current_symbol} = [ @params ];
5712                 } else {
5713                     # Delete any existing params in case we are overriding a
5714                     # previously read template.
5715                     delete $SymbolParams{$current_symbol};
5716                 }
5717             }
5718             $current_type = $type;
5719             $current_symbol = $symbol;
5720             $current_param = -1;
5721             $in_unused_params = 0;
5722             $in_deprecated = 0;
5723             $in_since = 0;
5724             $in_stability = 0;
5725             $symbol_doc = "";
5726             @params = ();
5728         } elsif (m/^<!-- # Unused Parameters # -->/) {
5729             #print "DEBUG: Found unused parameters\n";
5730             $in_unused_params = 1;
5731             next;
5733         } elsif ($in_unused_params && $skip_unused_params) {
5734             # When outputting the DocBook we skip unused parameters.
5735             #print "DEBUG: Skipping unused param: $_";
5736             next;
5738         } else {
5739             # Check if param found. Need to handle "..." and "format...".
5740             if (s/^\@([\w\.]+):\040?//) {
5741                 my $param_name = $1;
5742                 my $param_desc = $_;
5743                 # Allow variations of 'Returns'
5744                 if ($param_name =~ m/^[Rr]eturns?$/) {
5745                     $param_name = "Returns";
5746                 }
5747                 # Allow varargs variations
5748                 if ($param_name =~ m/^.*\.\.\.$/) {
5749                     $param_name = "...";
5750                 }
5752                 # strip trailing whitespaces and blank lines
5753                 s/\s+\n$/\n/m;
5754                 s/\n+$/\n/sm;
5755                 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5757                 if ($param_name eq "Deprecated") {
5758                     $in_deprecated = 1;
5759                     $Deprecated{$current_symbol} = $_;
5760                 } elsif ($param_name eq "Since") {
5761                     $in_since = 1;
5762                     chomp;
5763                     $Since{$current_symbol} = $_;
5764                 } elsif ($param_name eq "Stability") {
5765                     $in_stability = 1;
5766                     $StabilityLevel{$current_symbol} = $_;
5767                 } else {
5768                     push (@params, $param_name);
5769                     push (@params, $param_desc);
5770                     $current_param += $PARAM_FIELD_COUNT;
5771                 }
5772             } else {
5773                 # strip trailing whitespaces and blank lines
5774                 s/\s+\n$/\n/m;
5775                 s/\n+$/\n/sm;
5777                 if (!m/^\s+$/) {
5778                     if ($in_deprecated) {
5779                         $Deprecated{$current_symbol} .= $_;
5780                     } elsif ($in_since) {
5781                         &LogWarning ($template, $., "multi-line since docs found");
5782                         #$Since{$current_symbol} .= $_;
5783                     } elsif ($in_stability) {
5784                         $StabilityLevel{$current_symbol} .= $_;
5785                     } elsif ($current_param >= 0) {
5786                         $params[$current_param] .= $_;
5787                     } else {
5788                         $symbol_doc .= $_;
5789                     }
5790                 }
5791             }
5792         }
5793     }
5795     # Remember to finish the current symbol doccs.
5796     if ($current_symbol ne "") {
5798         $symbol_doc =~ s/\s+$//;
5799         $SymbolTypes{$current_symbol} = $current_type;
5800         $SymbolDocs{$current_symbol} = $symbol_doc;
5802         # Check that the stability level is valid.
5803         if ($StabilityLevel{$current_symbol}) {
5804             $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5805         }
5807         if ($current_param >= 0) {
5808             $SymbolParams{$current_symbol} = [ @params ];
5809         } else {
5810             # Delete any existing params in case we are overriding a
5811             # previously read template.
5812             delete $SymbolParams{$current_symbol};
5813         }
5814     }
5816     close (DOCS);
5817     return 1;
5821 #############################################################################
5822 # Function    : ReadObjectHierarchy
5823 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5824 #               the GtkObject subclasses described in this module (and their
5825 #               ancestors).
5826 #               It places them in the @Objects array, and places their level
5827 #               in the object hierarchy in the @ObjectLevels array, at the
5828 #               same index. GtkObject, the root object, has a level of 1.
5830 #               This also generates tree_index.sgml as it goes along.
5832 # Arguments   : none
5833 #############################################################################
5835 sub ReadObjectHierarchy {
5836     @Objects = ();
5837     @ObjectLevels = ();
5839     if (! -f $OBJECT_TREE_FILE) {
5840         return;
5841     }
5842     if (!open (INPUT, $OBJECT_TREE_FILE)) {
5843         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5844         return;
5845     }
5847     # Only emit objects if they are supposed to be documented, or if
5848     # they have documented children. To implement this, we maintain a
5849     # stack of pending objects which will be emitted if a documented
5850     # child turns up.
5851     my @pending_objects = ();
5852     my @pending_levels = ();
5853     my $root;
5854     my @tree = ();
5855     while (<INPUT>) {
5856         if (m/\S+/) {
5857             my $object = $&;
5858             my $level = (length($`)) / 2 + 1;
5859             my $xref = "";
5861             if ($level == 1) {
5862                 $root = $object;
5863             }
5865             while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5866                 my $pobject = pop(@pending_objects);
5867                 my $plevel = pop(@pending_levels);
5868             }
5870             push (@pending_objects, $object);
5871             push (@pending_levels, $level);
5873             if (exists($KnownSymbols{$object})) {
5874                 while ($#pending_levels >= 0) {
5875                     $object = shift @pending_objects;
5876                     $level = shift @pending_levels;
5877                     $xref = &MakeXRef ($object);
5879                     push (@tree, ' ' x ($level * 4) . "$xref");
5880                     push (@Objects, $object);
5881                     push (@ObjectLevels, $level);
5882                     $ObjectRoots{$object} = $root;
5883                 }
5884             }
5885             #else {
5886             #    LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5887             #}
5888         }
5889     }
5890     close (INPUT);
5892     # FIXME: use $OUTPUT_FORMAT
5893     # my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.$OUTPUT_FORMAT";
5894     my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml";
5895     my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new";
5897     open (OUTPUT, ">$new_tree_index")
5898         || die "Can't create $new_tree_index: $!";
5900     if ($OUTPUT_FORMAT eq "xml") {
5901         my $tree_header = $doctype_header;
5903         $tree_header =~ s/<!DOCTYPE \w+/<!DOCTYPE screen/;
5904         print (OUTPUT "$tree_header");
5905     }
5906     print (OUTPUT "<screen>\n" . &AddTreeLineArt(\@tree) . "\n</screen>\n");
5907     close (OUTPUT);
5909     &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5911     &OutputObjectList;
5914 #############################################################################
5915 # Function    : ReadInterfaces
5916 # Description : This reads in the $MODULE.interfaces file.
5918 # Arguments   : none
5919 #############################################################################
5921 sub ReadInterfaces {
5922     %Interfaces = ();
5924     if (! -f $INTERFACES_FILE) {
5925         return;
5926     }
5927     if (!open (INPUT, $INTERFACES_FILE)) {
5928         warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5929         return;
5930     }
5932     while (<INPUT>) {
5933        chomp;
5934        my ($object, @ifaces) = split;
5935        if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5936            my @knownIfaces = ();
5938            # filter out private interfaces, but leave foreign interfaces
5939            foreach my $iface (@ifaces) {
5940                if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5941                    push (@knownIfaces, $iface);
5942                }
5943              }
5945            $Interfaces{$object} = join(' ', @knownIfaces);
5946        }
5947     }
5948     close (INPUT);
5951 #############################################################################
5952 # Function    : ReadPrerequisites
5953 # Description : This reads in the $MODULE.prerequisites file.
5955 # Arguments   : none
5956 #############################################################################
5958 sub ReadPrerequisites {
5959     %Prerequisites = ();
5961     if (! -f $PREREQUISITES_FILE) {
5962         return;
5963     }
5964     if (!open (INPUT, $PREREQUISITES_FILE)) {
5965         warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
5966         return;
5967     }
5969     while (<INPUT>) {
5970        chomp;
5971        my ($iface, @prereqs) = split;
5972        if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
5973            my @knownPrereqs = ();
5975            # filter out private prerequisites, but leave foreign prerequisites
5976            foreach my $prereq (@prereqs) {
5977                if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
5978                   push (@knownPrereqs, $prereq);
5979                }
5980            }
5982            $Prerequisites{$iface} = join(' ', @knownPrereqs);
5983        }
5984     }
5985     close (INPUT);
5988 #############################################################################
5989 # Function    : ReadArgsFile
5990 # Description : This reads in an existing file which contains information on
5991 #                all GTK args. It creates the arrays @ArgObjects, @ArgNames,
5992 #                @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
5993 #               on the args.
5994 # Arguments   : $file - the file containing the arg information.
5995 #############################################################################
5997 sub ReadArgsFile {
5998     my ($file) = @_;
6000     my $in_arg = 0;
6001     my $arg_object;
6002     my $arg_name;
6003     my $arg_type;
6004     my $arg_flags;
6005     my $arg_nick;
6006     my $arg_blurb;
6007     my $arg_default;
6008     my $arg_range;
6010     # Reset the args info.
6011     @ArgObjects = ();
6012     @ArgNames = ();
6013     @ArgTypes = ();
6014     @ArgFlags = ();
6015     @ArgNicks = ();
6016     @ArgBlurbs = ();
6017     @ArgDefaults = ();
6018     @ArgRanges = ();
6020     if (! -f $file) {
6021         return;
6022     }
6023     if (!open (INPUT, $file)) {
6024         warn "Can't open $file - skipping args\n";
6025         return;
6026     }
6027     while (<INPUT>) {
6028         if (!$in_arg) {
6029             if (m/^<ARG>/) {
6030                 $in_arg = 1;
6031                 $arg_object = "";
6032                 $arg_name = "";
6033                 $arg_type = "";
6034                 $arg_flags = "";
6035                 $arg_nick = "";
6036                 $arg_blurb = "";
6037                 $arg_default = "";
6038                 $arg_range = "";
6039             }
6040         } else {
6041             if (m/^<NAME>(.*)<\/NAME>/) {
6042                 $arg_name = $1;
6043                 if ($arg_name =~ m/^(.*)::(.*)$/) {
6044                     $arg_object = $1;
6045                     ($arg_name = $2) =~ s/_/-/g;
6046                     #print "Found arg: $arg_name\n";
6047                 } else {
6048                     &LogWarning ($file, $., "Invalid argument name: $arg_name");
6049                 }
6050             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6051                 $arg_type = $1;
6052             } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6053                 $arg_range = $1;
6054             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6055                 $arg_flags = $1;
6056             } elsif (m/^<NICK>(.*)<\/NICK>/) {
6057                 $arg_nick = $1;
6058             } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6059                 $arg_blurb = $1;
6060                 if ($arg_blurb eq "(null)") {
6061                   $arg_blurb = "";
6062                   &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6063                 }
6064             } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6065                 $arg_default = $1;
6066             } elsif (m%^</ARG>%) {
6067                 #print "Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n";
6068                 push (@ArgObjects, $arg_object);
6069                 push (@ArgNames, $arg_name);
6070                 push (@ArgTypes, $arg_type);
6071                 push (@ArgRanges, $arg_range);
6072                 push (@ArgFlags, $arg_flags);
6073                 push (@ArgNicks, $arg_nick);
6074                 push (@ArgBlurbs, $arg_blurb);
6075                 push (@ArgDefaults, $arg_default);
6076                 $in_arg = 0;
6077             }
6078         }
6079     }
6080     close (INPUT);
6083 #############################################################################
6084 # Function    : AddTreeLineArt
6085 # Description : Add unicode lineart to a pre-indented string array and returns
6086 #               it as as multiline string.
6087 # Arguments   : @tree - array of indented strings.
6088 #############################################################################
6090 sub AddTreeLineArt {
6091   my @tree = @{$_[0]};
6092   my $i;
6093   my $j;
6094   my $indent;
6095   
6096   # iterate bottom up over the tree 
6097   for ($i = $#tree; $i >= 0; $i--) {
6098     # count leading spaces
6099     $tree[$i] =~ /^([^<A-Za-z]*)/;
6100     $indent = length( $1 );
6101     # replace with ╰───, if place of ╰ is not space insert ├
6102     if ($indent > 4) {
6103       if (substr($tree[$i],$indent-4,1) eq " ") {
6104         substr($tree[$i],$indent-4,4) = "--- ";
6105       } else {
6106         substr($tree[$i],$indent-4,4) = "+-- ";
6107       }
6108       # go lines up while space and insert |
6109       for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6110         substr($tree[$j],$indent-4,1) = '|';
6111       }
6112     }
6113   }
6114   
6115   my $res = join("\n", @tree);
6116   # unicode chars for: ╰──
6117   $res =~ s%---%<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>%g;
6118   # unicde chars for: ├──
6119   $res =~ s%\+--%<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>%g;
6120   # unicode char for: │
6121   $res =~ s%\|%<phrase role=\"lineart\">&#9474;</phrase>%g;
6122   
6123   return $res;
6127 #############################################################################
6128 # Function    : CheckIsObject
6129 # Description : Returns 1 if the given name is a GObject or a subclass.
6130 #                It uses the global @Objects array.
6131 #                Note that the @Objects array only contains classes in the
6132 #                current module and their ancestors - not all GObject classes.
6133 # Arguments   : $name - the name to check.
6134 #############################################################################
6136 sub CheckIsObject {
6137     my ($name) = @_;
6138     my $root = $ObjectRoots{$name};
6139     # Let GBoxed pass as an object here to get -struct appended to the id
6140     # and prevent conflicts with sections.
6141     return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6145 #############################################################################
6146 # Function    : MakeReturnField
6147 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6148 # Arguments   : $str - the string to pad.
6149 #############################################################################
6151 sub MakeReturnField {
6152     my ($str) = @_;
6154     return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6157 #############################################################################
6158 # Function    : GetSymbolSourceFile
6159 # Description : Get the filename where the symbol docs where taken from.
6160 # Arguments   : $symbol - the symbol name
6161 #############################################################################
6163 sub GetSymbolSourceFile {
6164     my ($symbol) = @_;
6166     if (defined($SourceSymbolSourceFile{$symbol})) {
6167         return $SourceSymbolSourceFile{$symbol};
6168     } elsif (defined($SymbolSourceFile{$symbol})) {
6169         return $SymbolSourceFile{$symbol};
6170     } else {
6171         return "";
6172     }
6175 #############################################################################
6176 # Function    : GetSymbolSourceLine
6177 # Description : Get the file line where the symbol docs where taken from.
6178 # Arguments   : $symbol - the symbol name
6179 #############################################################################
6181 sub GetSymbolSourceLine {
6182     my ($symbol) = @_;
6184     if (defined($SourceSymbolSourceLine{$symbol})) {
6185         return $SourceSymbolSourceLine{$symbol};
6186     } elsif (defined($SymbolSourceLine{$symbol})) {
6187         return $SymbolSourceLine{$symbol};
6188     } else {
6189         return 0;
6190     }