Updated Spanish translation
[gtk-doc.git] / gtkdoc-mkdb.in
blobde0d35d016934b3a3f67267783d5a63017ec6d6b
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 @TRACE@(" 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     'nullable' => "NULL may be passed as the value in, out, in-out; or as a return value.",
308     'optional' => "NULL may be passed instead of a pointer to a location.",
309     'array' => "Parameter points to an array of items.",
310     'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
311     'attributes' => "Free-form key-value pairs.",
312     'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
313     'constructor' => "This symbol is a constructor, not a static method.",
314     'destroy' => "This parameter is a 'destroy_data', for callbacks.",
315     'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
316     'element-type' => "Generics and defining elements of containers and arrays.",
317     'error-domains' => "Typed errors. Similar to throws in Java.",
318     'foreign' => "This is a foreign struct.",
319     'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
320     'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
321     'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
322     'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
323     'method' => "This is a method",
324     'not-error' => "A GError parameter is not to be handled like a normal GError.",
325     'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
326     'out caller-allocates' => "Out parameter, where caller must allocate storage.",
327     'out callee-allocates' => "Out parameter, where caller must allocate storage.",
328     'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
329     'rename-to' => "Rename the original symbol's name to SYMBOL.",
330     'scope call' => "The callback is valid only during the call to the method.",
331     'scope async' => "The callback is valid until first called.",
332     'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
333     'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
334     'skip' => "Exposed in C code, not necessarily available in other languages.",
335     'transfer container' => "Free data container after the code is done.",
336     'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
337     'transfer full' => "Free data after the code is done.",
338     'transfer none' => "Don't free data after the code is done.",
339     'type' => "Override the parsed C type with given type.",
340     'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
341     'virtual' => "This is the invoker for a virtual method.",
342     'value' => "The specified value overrides the evaluated value of the constant.",
343     # Stability Level definition
344     # https://bugzilla.gnome.org/show_bug.cgi?id=170860
345     'Stable' => <<EOF,
346 The intention of a Stable interface is to enable arbitrary third parties to
347 develop applications to these interfaces, release them, and have confidence that
348 they will run on all minor releases of the product (after the one in which the
349 interface was introduced, and within the same major release). Even at a major
350 release, incompatible changes are expected to be rare, and to have strong
351 justifications.
353     'Unstable' => <<EOF,
354 Unstable interfaces are experimental or transitional. They are typically used to
355 give outside developers early access to new or rapidly changing technology, or
356 to provide an interim solution to a problem where a more general solution is
357 anticipated. No claims are made about either source or binary compatibility from
358 one minor release to the next.
360 The Unstable interface level is a warning that these interfaces are  subject to
361 change without warning and should not be used in unbundled products.
363 Given such caveats, customer impact need not be a factor when considering
364 incompatible changes to an Unstable interface in a major or minor release.
365 Nonetheless, when such changes are introduced, the changes should still be
366 mentioned in the release notes for the affected release.
368     'Private' => <<EOF
369 An interface that can be used within the GNOME stack itself, but that is not
370 documented for end-users.  Such functions should only be used in specified and
371 documented ways.
375 # Elements to consider non-block items in MarkDown parsing
376 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
377                                "emphasis" => 1,
378                                "envar" => 1,
379                                "filename" => 1,
380                                "firstterm" => 1,
381                                "function" => 1,
382                                "manvolnum" => 1,
383                                "option" => 1,
384                                "replaceable" => 1,
385                                "structname" => 1,
386                                "title" => 1,
387                                "varname" => 1 );
388 my %MD_ESCAPABLE_CHARS = ( "\\" => 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                            "." => 1,
403                            "!" => 1 );
404 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
405                                "%" => 1 );
407 # Create the root DocBook output directory if it doens't exist.
408 if (! -e $SGML_OUTPUT_DIR) {
409     mkdir ("$SGML_OUTPUT_DIR", 0777)
410         || die "Can't create directory: $SGML_OUTPUT_DIR";
413 # Function and other declaration output settings.
414 my $RETURN_TYPE_FIELD_WIDTH = 20;
415 my $SYMBOL_FIELD_WIDTH = 36;
416 my $MAX_SYMBOL_FIELD_WIDTH = 40;
417 my $SIGNAL_FIELD_WIDTH = 16;
418 my $PARAM_FIELD_COUNT = 2;
420 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
421 &ReadSignalsFile ($SIGNALS_FILE);
422 &ReadArgsFile ($ARGS_FILE);
423 &ReadObjectHierarchy;
424 &ReadInterfaces;
425 &ReadPrerequisites;
427 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
428 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
429     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
432 for my $dir (@SOURCE_DIRS) {
433     &ReadSourceDocumentation ($dir);
436 my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt");
438 # If any of the DocBook SGML files have changed, update the timestamp file (so
439 # it can be used for Makefile dependencies).
440 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
442     # try to detect the common prefix
443     # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
444     if ($NAME_SPACE eq "") {
445         $NAME_SPACE="";
446         my $pos=0;
447         my $ratio=0.0;
448         do {
449             my %prefix;
450             my $letter="";
451             foreach my $symbol (keys(%IndexEntriesFull)) {
452                 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
453                     if (length($symbol)>$pos) {
454                         $letter=substr($symbol,$pos,1);
455                         # stop prefix scanning
456                         if ($letter eq "_") {
457                             # stop on "_"
458                             last;
459                         }
460                         # Should we also stop on a uppercase char, if last was lowercase
461                         #   GtkWidget, if we have the 'W' and had the 't' before
462                         # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
463                         #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
464                         # need to recound each time as this is per symbol
465                         $prefix{uc($letter)}++;
466                     }
467                 }
468             }
469             if ($letter ne "" && $letter ne "_") {
470                 my $maxletter="";
471                 my $maxsymbols=0;
472                 foreach $letter (keys(%prefix)) {
473                     #print "$letter: $prefix{$letter}.\n";
474                     if ($prefix{$letter}>$maxsymbols) {
475                         $maxletter=$letter;
476                         $maxsymbols=$prefix{$letter};
477                     }
478                 }
479                 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
480                 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
481                 if ($ratio > 0.9) {
482                     # do another round
483                     $NAME_SPACE .= $maxletter;
484                 }
485                 $pos++;
486             }
487             else {
488                 $ratio=0.0;
489             }
490         } while ($ratio > 0.9);
491         #print "most symbols start with $NAME_SPACE\n";
492     }
494     &OutputIndexFull;
495     &OutputDeprecatedIndex;
496     &OutputSinceIndexes;
497     &OutputAnnotationGlossary;
499     open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
500         || die "Can't create $ROOT_DIR/sgml.stamp: $!";
501     print (TIMESTAMP "timestamp");
502     close (TIMESTAMP);
505 #############################################################################
506 # Function    : OutputObjectList
507 # Description : This outputs the alphabetical list of objects, in a columned
508 #                table.
509 #               FIXME: Currently this also outputs ancestor objects
510 #                which may not actually be in this module.
511 # Arguments   : none
512 #############################################################################
514 sub OutputObjectList {
515     my $cols = 3;
517     # FIXME: use $OUTPUT_FORMAT
518     # my $old_object_index = "$SGML_OUTPUT_DIR/object_index.$OUTPUT_FORMAT";
519     my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml";
520     my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new";
522     open (OUTPUT, ">$new_object_index")
523         || die "Can't create $new_object_index: $!";
525     if ($OUTPUT_FORMAT eq "xml") {
526         my $header = $doctype_header;
528         $header =~ s/<!DOCTYPE \w+/<!DOCTYPE informaltable/;
529         print (OUTPUT "$header");
530     }
532     print (OUTPUT <<EOF);
533 <informaltable pgwide="1" frame="none">
534 <tgroup cols="$cols">
535 <colspec colwidth="1*"${empty_element_end}
536 <colspec colwidth="1*"${empty_element_end}
537 <colspec colwidth="1*"${empty_element_end}
538 <tbody>
541     my $count = 0;
542     my $object;
543     foreach $object (sort (@Objects)) {
544         my $xref = &MakeXRef ($object);
545         if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
546         print (OUTPUT "<entry>$xref</entry>\n");
547         if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
548         $count++;
549     }
550     if ($count == 0) {
551         # emit an empty row, since empty tables are invalid
552         print (OUTPUT "<row><entry> </entry></row>\n");
553     }
554     else {
555         if ($count % $cols > 0) {
556             print (OUTPUT "</row>\n");
557         }
558     }
560     print (OUTPUT <<EOF);
561 </tbody></tgroup></informaltable>
563     close (OUTPUT);
565     &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
568 #############################################################################
569 # Function    : TrimTextBlock
570 # Description : Trims extra whitespace. Empty lines inside a block are
571 #                preserved.
572 # Arguments   : $desc - the text block to trim. May contain newlines.
573 #############################################################################
575 sub TrimTextBlock {
576   my ($desc) = @_;
577   
578   # strip leading spaces on the block
579   $desc =~ s/^\s+//s;
580   # strip trailing spaces on every line
581   $desc =~ s/\s+$/\n/mg;
582   
583   return $desc;
587 #############################################################################
588 # Function    : OutputSGML
589 # Description : This collects the output for each section of the docs, and
590 #                outputs each file when the end of the section is found.
591 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
592 #                the functions/macros/structs etc. being documented, organised
593 #                into sections and subsections.
594 #############################################################################
596 sub OutputSGML {
597     my ($file) = @_;
599     @TRACE@("Reading: $file\n");
600     open (INPUT, $file)
601         || die "Can't open $file: $!";
602     my $filename = "";
603     my $book_top = "";
604     my $book_bottom = "";
605     my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
606     my $section_includes = "";
607     my $in_section = 0;
608     my $title = "";
609     my $section_id = "";
610     my $subsection = "";
611     my $num_symbols;
612     my $changed = 0;
613     my $functions_synop = "";
614     my $other_synop = "";
615     my $functions_details = "";
616     my $other_details = "";
617     my $signals_synop = "";
618     my $signals_desc = "";
619     my $args_synop = "";
620     my $child_args_synop = "";
621     my $style_args_synop = "";
622     my $args_desc = "";
623     my $child_args_desc = "";
624     my $style_args_desc = "";
625     my $hierarchy_str = "";
626     my @hierarchy = ();
627     my $interfaces = "";
628     my $implementations = "";
629     my $prerequisites = "";
630     my $derived = "";
631     my @file_objects = ();
632     my %templates = ();
633     my %symbol_def_line = ();
635     # merge the source docs, in case there are no templates
636     &MergeSourceDocumentation;
638     while (<INPUT>) {
639         if (m/^#/) {
640             next;
642         } elsif (m/^<SECTION>/) {
643             $num_symbols = 0;
644             $in_section = 1;
645             @file_objects = ();
646             %symbol_def_line = ();
648         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
649             $other_synop .= "\n";
650             $functions_synop .= "\n";
651             $subsection = $1;
653         } elsif (m/^<SUBSECTION>/) {
655         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
656             $title = $1;
657             @TRACE@("Section: $title\n");
659             # We don't want warnings if object & class structs aren't used.
660             $DeclarationOutput{$title} = 1;
661             $DeclarationOutput{"${title}Class"} = 1;
662             $DeclarationOutput{"${title}Iface"} = 1;
663             $DeclarationOutput{"${title}Interface"} = 1;
665         } elsif (m/^<FILE>(.*)<\/FILE>/) {
666             $filename = $1;
667             if (! defined $templates{$filename}) {
668                if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
669                    &MergeSourceDocumentation;
670                    $templates{$filename}=$.;
671                }
672             } else {
673                 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
674                     "Previous occurrence on line ".$templates{$filename}.".");
675             }
676             if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
677                 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
678                  # Remove trailing blanks
679                 $title =~ s/\s+$//;
680            }
682         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
683             if ($in_section) {
684                 $section_includes = $1;
685             } else {
686                 if (defined $DEFAULT_INCLUDES) {
687                     &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
688                 }
689                 else {
690                     $includes = $1;
691                 }
692             }
694         } elsif (m/^<\/SECTION>/) {
695             @TRACE@("End of section: $title\n");
696             if ($num_symbols > 0) {
697                 # collect documents
698                 if ($OUTPUT_FORMAT eq "xml") {
699                     $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
700                 } else {
701                     $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n";
702                     $book_bottom .= "    &$section_id;\n";
703                 }
705                 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
706                     if ($section_includes) {
707                         &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
708                     }
709                     $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
710                 }
711                 if ($section_includes eq "") {
712                     $section_includes = $includes;
713                 }
715                  $signals_synop =~ s/^\n*//g;
716                  $signals_synop =~ s/\n+$/\n/g;
717                 if ($signals_synop ne '') {
718                     $signals_synop = <<EOF;
719 <refsect1 id="$section_id.signals" role="signal_proto">
720 <title role="signal_proto.title">Signals</title>
721 <informaltable frame="none">
722 <tgroup cols="3">
723 <colspec colname="signals_return" colwidth="150px"/>
724 <colspec colname="signals_name" colwidth="300px"/>
725 <colspec colname="signals_flags" colwidth="200px"/>
726 <tbody>
727 ${signals_synop}
728 </tbody>
729 </tgroup>
730 </informaltable>
731 </refsect1>
733                      $signals_desc = TrimTextBlock($signals_desc);
734                     $signals_desc  = <<EOF;
735 <refsect1 id="$section_id.signal-details" role="signals">
736 <title role="signals.title">Signal Details</title>
737 $signals_desc
738 </refsect1>
740                 }
742                 $args_synop =~ s/^\n*//g;
743                 $args_synop =~ s/\n+$/\n/g;
744                 if ($args_synop ne '') {
745                     $args_synop = <<EOF;
746 <refsect1 id="$section_id.properties" role="properties">
747 <title role="properties.title">Properties</title>
748 <informaltable frame="none">
749 <tgroup cols="3">
750 <colspec colname="properties_type" colwidth="150px"/>
751 <colspec colname="properties_name" colwidth="300px"/>
752 <colspec colname="properties_flags" colwidth="200px"/>
753 <tbody>
754 ${args_synop}
755 </tbody>
756 </tgroup>
757 </informaltable>
758 </refsect1>
760                      $args_desc = TrimTextBlock($args_desc);
761                     $args_desc  = <<EOF;
762 <refsect1 id="$section_id.property-details" role="property_details">
763 <title role="property_details.title">Property Details</title>
764 $args_desc
765 </refsect1>
767                 }
769                 $child_args_synop =~ s/^\n*//g;
770                 $child_args_synop =~ s/\n+$/\n/g;
771                 if ($child_args_synop ne '') {
772                     $args_synop .= <<EOF;
773 <refsect1 id="$section_id.child-properties" role="child_properties">
774 <title role="child_properties.title">Child Properties</title>
775 <informaltable frame="none">
776 <tgroup cols="3">
777 <colspec colname="child_properties_type" colwidth="150px"/>
778 <colspec colname="child_properties_name" colwidth="300px"/>
779 <colspec colname="child_properties_flags" colwidth="200px"/>
780 <tbody>
781 ${child_args_synop}
782 </tbody>
783 </tgroup>
784 </informaltable>
785 </refsect1>
787                      $child_args_desc = TrimTextBlock($child_args_desc);
788                      $args_desc .= <<EOF;
789 <refsect1 id="$section_id.child-property-details" role="child_property_details">
790 <title role="child_property_details.title">Child Property Details</title>
791 $child_args_desc
792 </refsect1>
794                 }
796                 $style_args_synop =~ s/^\n*//g;
797                 $style_args_synop =~ s/\n+$/\n/g;
798                 if ($style_args_synop ne '') {
799                     $args_synop .= <<EOF;
800 <refsect1 id="$section_id.style-properties" role="style_properties">
801 <title role="style_properties.title">Style Properties</title>
802 <informaltable frame="none">
803 <tgroup cols="3">
804 <colspec colname="style_properties_type" colwidth="150px"/>
805 <colspec colname="style_properties_name" colwidth="300px"/>
806 <colspec colname="style_properties_flags" colwidth="200px"/>
807 <tbody>
808 ${style_args_synop}
809 </tbody>
810 </tgroup>
811 </informaltable>
812 </refsect1>
814                      $style_args_desc = TrimTextBlock($style_args_desc);
815                     $args_desc .= <<EOF;
816 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
817 <title role="style_properties_details.title">Style Property Details</title>
818 $style_args_desc
819 </refsect1>
821                 }
823                 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
824                 if ($hierarchy_str ne "") {
825                     $hierarchy_str = <<EOF;
826 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
827 <title role="object_hierarchy.title">Object Hierarchy</title>
828 <screen>$hierarchy_str
829 </screen>
830 </refsect1>
832                 }
834                  $interfaces =~ TrimTextBlock($interfaces);
835                 if ($interfaces ne "") {
836                     $interfaces = <<EOF;
837 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
838 <title role="impl_interfaces.title">Implemented Interfaces</title>
839 $interfaces
840 </refsect1>
842                 }
844                  $implementations = TrimTextBlock($implementations);
845                 if ($implementations ne "") {
846                     $implementations = <<EOF;
847 <refsect1 id="$section_id.implementations" role="implementations">
848 <title role="implementations.title">Known Implementations</title>
849 $implementations
850 </refsect1>
852                 }
854                  $prerequisites = TrimTextBlock($prerequisites);
855                 if ($prerequisites ne "") {
856                     $prerequisites = <<EOF;
857 <refsect1 id="$section_id.prerequisites" role="prerequisites">
858 <title role="prerequisites.title">Prerequisites</title>
859 $prerequisites
860 </refsect1>
862                 }
864                  $derived = TrimTextBlock($derived);
865                 if ($derived ne "") {
866                     $derived = <<EOF;
867 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
868 <title role="derived_interfaces.title">Known Derived Interfaces</title>
869 $derived
870 </refsect1>
872                 }
874                 $functions_synop =~ s/^\n*//g;
875                 $functions_synop =~ s/\n+$/\n/g;
876                 if ($functions_synop ne '') {
877                   $functions_synop = <<EOF;
878 <refsect1 id="$section_id.functions" role="functions_proto">
879 <title role="functions_proto.title">Functions</title>
880 <informaltable pgwide="1" frame="none">
881 <tgroup cols="2">
882 <colspec colname="functions_return" colwidth="150px"/>
883 <colspec colname="functions_name"/>
884 <tbody>
885 ${functions_synop}
886 </tbody>
887 </tgroup>
888 </informaltable>
889 </refsect1>
891                 }
893                 $other_synop =~ s/^\n*//g;
894                 $other_synop =~ s/\n+$/\n/g;
895                 if ($other_synop ne '') {
896                   $other_synop = <<EOF;
897 <refsect1 id="$section_id.other" role="other_proto">
898 <title role="other_proto.title">Types and Values</title>
899 <informaltable role="enum_members_table" pgwide="1" frame="none">
900 <tgroup cols="2">
901 <colspec colname="name" colwidth="150px"/>
902 <colspec colname="description"/>
903 <tbody>
904 ${other_synop}
905 </tbody>
906 </tgroup>
907 </informaltable>
908 </refsect1>
910                 }
912                 my $file_changed = &OutputSGMLFile ($filename, $title, $section_id,
913                                                     $section_includes,
914                                                     \$functions_synop, \$other_synop,
915                                                     \$functions_details, \$other_details,
916                                                     \$signals_synop, \$signals_desc,
917                                                     \$args_synop, \$args_desc,
918                                                     \$hierarchy_str, \$interfaces,
919                                                     \$implementations,
920                                                     \$prerequisites, \$derived,
921                                                     \@file_objects);
922                 if ($file_changed) {
923                     $changed = 1;
924                 }
925             }
926             $title = "";
927             $section_id = "";
928             $subsection = "";
929             $in_section = 0;
930             $section_includes = "";
931             $functions_synop = "";
932             $other_synop = "";
933             $functions_details = "";
934             $other_details = "";
935             $signals_synop = "";
936             $signals_desc = "";
937             $args_synop = "";
938             $child_args_synop = "";
939             $style_args_synop = "";
940             $args_desc = "";
941             $child_args_desc = "";
942             $style_args_desc = "";
943             $hierarchy_str = "";
944             @hierarchy = ();
945             $interfaces = "";
946             $implementations = "";
947             $prerequisites = "";
948             $derived = "";
950         } elsif (m/^(\S+)/) {
951             my $symbol = $1;
952             @TRACE@("  Symbol: $symbol in subsection: $subsection\n");
954             # check for duplicate entries
955             if (! defined $symbol_def_line{$symbol}) {
956                 my $declaration = $Declarations{$symbol};
957                 if (defined ($declaration)) {
958                     if (&CheckIsObject ($symbol)) {
959                         push @file_objects, $symbol;
960                     }
961                     # We don't want standard macros/functions of GObjects,
962                     # or private declarations.
963                     if ($subsection ne "Standard" && $subsection ne "Private") {
964                         my ($synop, $desc) = &OutputDeclaration ($symbol,
965                                                                  $declaration);
966                         my $type = $DeclarationTypes {$symbol};
967         
968                         if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
969                           $functions_synop .= $synop;
970                           $functions_details .= $desc;
971                         } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
972                           $functions_synop .= $synop;
973                           $functions_details .= $desc;
974                         } else {
975                           $other_synop .= $synop;
976                           $other_details .= $desc;
977                         }
978                     }
979                     my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
980                     my ($arg_synop, $child_arg_synop, $style_arg_synop,
981                         $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
982                     my $ifaces = &GetInterfaces ($symbol);
983                     my $impls = &GetImplementations ($symbol);
984                     my $prereqs = &GetPrerequisites ($symbol);
985                     my $der = &GetDerived ($symbol);
986                     @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
988                     $signals_synop .= $sig_synop;
989                     $signals_desc .= $sig_desc;
990                     $args_synop .= $arg_synop;
991                     $child_args_synop .= $child_arg_synop;
992                     $style_args_synop .= $style_arg_synop;
993                     $args_desc .= $arg_desc;
994                     $child_args_desc .= $child_arg_desc;
995                     $style_args_desc .= $style_arg_desc;
996                     $interfaces .= $ifaces;
997                     $implementations .= $impls;
998                     $prerequisites .= $prereqs;
999                     $derived .= $der;
1001                     # Note that the declaration has been output.
1002                     $DeclarationOutput{$symbol} = 1;
1003                 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
1004                     $UndeclaredSymbols{$symbol} = 1;
1005                     &LogWarning ($file, $., "No declaration found for $symbol.");
1006                 }
1007                 $num_symbols++;
1008                 $symbol_def_line{$symbol}=$.;
1010                 if ($section_id eq "") {
1011                     if($title eq "" && $filename eq "") {
1012                         &LogWarning ($file, $., "Section has no title and no file.");
1013                     }
1014                     # FIXME: one of those would be enough
1015                     # filename should be an internal detail for gtk-doc
1016                     if ($title eq "") {
1017                         $title = $filename;
1018                     } elsif ($filename eq "") {
1019                         $filename = $title;
1020                     }
1021                     $filename =~ s/\s/_/g;
1023                     $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1024                     if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1025                         # Remove trailing blanks and use as is
1026                         $section_id =~ s/\s+$//;
1027                     } elsif (&CheckIsObject ($title)) {
1028                         # GObjects use their class name as the ID.
1029                         $section_id = &CreateValidSGMLID ($title);
1030                     } else {
1031                         $section_id = &CreateValidSGMLID ("$MODULE-$title");
1032                     }
1033                 }
1034                 $SymbolSection{$symbol}=$title;
1035                 $SymbolSectionId{$symbol}=$section_id;
1036             }
1037             else {
1038                 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1039                     "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1040             }
1041         }
1042     }
1043     close (INPUT);
1045     &OutputMissingDocumentation;
1046     &OutputUndeclaredSymbols;
1047     &OutputUnusedSymbols;
1049     if ($OUTPUT_ALL_SYMBOLS) {
1050         &OutputAllSymbols;
1051     }
1052     if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1053         &OutputSymbolsWithoutSince;
1054     }
1056     for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1057         my $file_changed = &OutputExtraFile ($filename);
1058         if ($file_changed) {
1059             $changed = 1;
1060         }
1061     }
1063     &OutputBook ($book_top, $book_bottom);
1065     return $changed;
1068 #############################################################################
1069 # Function    : OutputIndex
1070 # Description : This writes an indexlist that can be included into the main-
1071 #               document into an <index> tag.
1072 #############################################################################
1074 sub OutputIndex {
1075     my ($basename, $apiindexref ) = @_;
1076     my %apiindex = %{$apiindexref};
1077     my $old_index = "$SGML_OUTPUT_DIR/$basename.xml";
1078     my $new_index = "$SGML_OUTPUT_DIR/$basename.new";
1079     my $lastletter = " ";
1080     my $divopen = 0;
1081     my $symbol;
1082     my $short_symbol;
1084     open (OUTPUT, ">$new_index")
1085         || die "Can't create $new_index";
1087     my $header = $doctype_header;
1088     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE indexdiv/;
1090     print (OUTPUT "$header<indexdiv>\n");
1092     @TRACE@("generate $basename index (".%apiindex." entries)\n");
1094     # do a case insensitive sort while chopping off the prefix
1095     foreach my $hash (
1096         sort { $$a{criteria} cmp $$b{criteria} }
1097         map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1098         keys %apiindex) {
1100         $symbol = $$hash{original};
1101         if (defined($$hash{short})) {
1102             $short_symbol = $$hash{short};
1103         } else {
1104             $short_symbol = $symbol;
1105         }
1107         # generate a short symbol description
1108         my $symbol_desc = "";
1109         my $symbol_section = "";
1110         my $symbol_section_id = "";
1111         my $symbol_type = "";
1112         if (defined($DeclarationTypes{$symbol})) {
1113           $symbol_type = lc($DeclarationTypes{$symbol});
1114         }
1115         if ($symbol_type eq "") {
1116             @TRACE@("trying symbol $symbol\n");
1117             if ($symbol =~ m/(.*)::(.*)/) {
1118                 my $oname = $1;
1119                 my $osym = $2;
1120                 my $i;
1121                 @TRACE@("  trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1122                 for ($i = 0; $i <= $#SignalNames; $i++) {
1123                     if ($SignalNames[$i] eq $osym) {
1124                         $symbol_type = "object signal";
1125                         if (defined($SymbolSection{$oname})) {
1126                            $symbol_section = $SymbolSection{$oname};
1127                            $symbol_section_id = $SymbolSectionId{$oname};
1128                         }
1129                         last;
1130                     }
1131                 }
1132             } elsif ($symbol =~ m/(.*):(.*)/) {
1133                 my $oname = $1;
1134                 my $osym = $2;
1135                 my $i;
1136                 @TRACE@("  trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1137                 for ($i = 0; $i <= $#ArgNames; $i++) {
1138                     @TRACE@("    ".$ArgNames[$i]."\n");
1139                     if ($ArgNames[$i] eq $osym) {
1140                         $symbol_type = "object property";
1141                         if (defined($SymbolSection{$oname})) {
1142                            $symbol_section = $SymbolSection{$oname};
1143                            $symbol_section_id = $SymbolSectionId{$oname};
1144                         }
1145                         last;
1146                     }
1147                 }
1148             }
1149         } else {
1150            if (defined($SymbolSection{$symbol})) {
1151                $symbol_section = $SymbolSection{$symbol};
1152                $symbol_section_id = $SymbolSectionId{$symbol};
1153            }
1154         }
1155         if ($symbol_type ne "") {
1156            $symbol_desc=", $symbol_type";
1157            if ($symbol_section ne "") {
1158                $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1159                #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1160            }
1161         }
1163         my $curletter = uc(substr($short_symbol,0,1));
1164         my $id = $apiindex{$symbol};
1166         @TRACE@("  add symbol $symbol with $id to index in section $curletter\n");
1168         if ($curletter ne $lastletter) {
1169             $lastletter = $curletter;
1171             if ($divopen == 1) {
1172                 print (OUTPUT "</indexdiv>\n");
1173             }
1174             print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1175             $divopen = 1;
1176         }
1178         print (OUTPUT <<EOF);
1179 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1181     }
1183     if ($divopen == 1) {
1184         print (OUTPUT "</indexdiv>\n");
1185     }
1186     print (OUTPUT "</indexdiv>\n");
1187     close (OUTPUT);
1189     &UpdateFileIfChanged ($old_index, $new_index, 0);
1193 #############################################################################
1194 # Function    : OutputIndexFull
1195 # Description : This writes the full api indexlist that can be included into the
1196 #               main document into an <index> tag.
1197 #############################################################################
1199 sub OutputIndexFull {
1200     &OutputIndex ("api-index-full", \%IndexEntriesFull);
1204 #############################################################################
1205 # Function    : OutputDeprecatedIndex
1206 # Description : This writes the deprecated api indexlist that can be included
1207 #               into the main document into an <index> tag.
1208 #############################################################################
1210 sub OutputDeprecatedIndex {
1211     &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1215 #############################################################################
1216 # Function    : OutputSinceIndexes
1217 # Description : This writes the 'since' api indexlists that can be included into
1218 #               the main document into an <index> tag.
1219 #############################################################################
1221 sub OutputSinceIndexes {
1222     my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1224     foreach my $version (@sinces) {
1225         @TRACE@("Since : [$version]\n");
1226         # TODO make filtered hash
1227         #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1228         my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1230         &OutputIndex ("api-index-$version", \%index);
1231     }
1234 #############################################################################
1235 # Function    : OutputAnnotationGlossary
1236 # Description : This writes a glossary of the used annotation terms into a
1237 #               separate glossary file that can be included into the main
1238 #               document.
1239 #############################################################################
1241 sub OutputAnnotationGlossary {
1242     my $old_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.xml";
1243     my $new_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.new";
1244     my $lastletter = " ";
1245     my $divopen = 0;
1247     # if there are no annotations used return
1248     return if (! keys(%AnnotationsUsed));
1250     # add acronyms that are referenced from acronym text
1251 rerun:
1252     foreach my $annotation (keys(%AnnotationsUsed)) {
1253         if(defined($AnnotationDefinition{$annotation})) {
1254             if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1255                 if (!exists($AnnotationsUsed{$1})) {
1256                     $AnnotationsUsed{$1} = 1;
1257                     goto rerun;
1258                 }
1259             }
1260         }
1261     }
1263     open (OUTPUT, ">$new_glossary")
1264         || die "Can't create $new_glossary";
1266     my $header = $doctype_header;
1267     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE glossary/;
1269     print (OUTPUT  <<EOF);
1270 $header
1271 <glossary id="annotation-glossary">
1272   <title>Annotation Glossary</title>
1275     foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1276         if(defined($AnnotationDefinition{$annotation})) {
1277             my $def = $AnnotationDefinition{$annotation};
1278             my $curletter = uc(substr($annotation,0,1));
1280             if ($curletter ne $lastletter) {
1281                 $lastletter = $curletter;
1283                 if ($divopen == 1) {
1284                     print (OUTPUT "</glossdiv>\n");
1285                 }
1286                 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1287                 $divopen = 1;
1288             }
1289             print (OUTPUT <<EOF);
1290     <glossentry>
1291       <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1292       <glossdef>
1293         <para>$def</para>
1294       </glossdef>
1295     </glossentry>
1297         }
1298     }
1300     if ($divopen == 1) {
1301         print (OUTPUT "</glossdiv>\n");
1302     }
1303     print (OUTPUT "</glossary>\n");
1304     close (OUTPUT);
1306     &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1309 #############################################################################
1310 # Function    : ReadKnownSymbols
1311 # Description : This collects the names of non-private symbols from the
1312 #               $MODULE-sections.txt file.
1313 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
1314 #                the functions/macros/structs etc. being documented, organised
1315 #                into sections and subsections.
1316 #############################################################################
1318 sub ReadKnownSymbols {
1319     my ($file) = @_;
1321     my $subsection = "";
1323     @TRACE@("Reading: $file\n");
1324     open (INPUT, $file)
1325         || die "Can't open $file: $!";
1327     while (<INPUT>) {
1328         if (m/^#/) {
1329             next;
1331         } elsif (m/^<SECTION>/) {
1332             $subsection = "";
1334         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1335             $subsection = $1;
1337         } elsif (m/^<SUBSECTION>/) {
1338             next;
1340         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1341             next;
1343         } elsif (m/^<FILE>(.*)<\/FILE>/) {
1344             $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1345             $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1346             next;
1348         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1349             next;
1351         } elsif (m/^<\/SECTION>/) {
1352             next;
1354         } elsif (m/^(\S+)/) {
1355             my $symbol = $1;
1357             if ($subsection ne "Standard" && $subsection ne "Private") {
1358                 $KnownSymbols{$symbol} = 1;
1359             }
1360             else {
1361                 $KnownSymbols{$symbol} = 0;
1362             }
1363         }
1364     }
1365     close (INPUT);
1369 #############################################################################
1370 # Function    : OutputDeclaration
1371 # Description : Returns the synopsis and detailed description DocBook
1372 #                describing one function/macro etc.
1373 # Arguments   : $symbol - the name of the function/macro begin described.
1374 #                $declaration - the declaration of the function/macro.
1375 #############################################################################
1377 sub OutputDeclaration {
1378     my ($symbol, $declaration) = @_;
1380     my $type = $DeclarationTypes {$symbol};
1381     if ($type eq 'MACRO') {
1382         return &OutputMacro ($symbol, $declaration);
1383     } elsif ($type eq 'TYPEDEF') {
1384         return &OutputTypedef ($symbol, $declaration);
1385     } elsif ($type eq 'STRUCT') {
1386         return &OutputStruct ($symbol, $declaration);
1387     } elsif ($type eq 'ENUM') {
1388         return &OutputEnum ($symbol, $declaration);
1389     } elsif ($type eq 'UNION') {
1390         return &OutputUnion ($symbol, $declaration);
1391     } elsif ($type eq 'VARIABLE') {
1392         return &OutputVariable ($symbol, $declaration);
1393     } elsif ($type eq 'FUNCTION') {
1394         return &OutputFunction ($symbol, $declaration, $type);
1395     } elsif ($type eq 'USER_FUNCTION') {
1396         return &OutputFunction ($symbol, $declaration, $type);
1397     } else {
1398         die "Unknown symbol type";
1399     }
1403 #############################################################################
1404 # Function    : OutputSymbolTraits
1405 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1406 # Arguments   : $symbol - the name of the function/macro begin described.
1407 #############################################################################
1409 sub OutputSymbolTraits {
1410     my ($symbol) = @_;
1411     my $desc = "";
1413     if (exists $Since{$symbol}) {
1414         $desc .= "<para role=\"since\">Since $Since{$symbol}</para>";
1415     }
1416     if (exists $StabilityLevel{$symbol}) {
1417         my $stability = $StabilityLevel{$symbol};
1418         $AnnotationsUsed{$stability} = 1;
1419         $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1420     }
1421     return $desc;
1424 #############################################################################
1425 # Function    : Output{Symbol,Section}ExtraLinks
1426 # Description : Returns extralinks for the symbol (if enabled).
1427 # Arguments   : $symbol - the name of the function/macro begin described.
1428 #############################################################################
1430 sub uri_escape {
1431     my $text = $_[0];
1432     return undef unless defined $text;
1434     # Build a char to hex map
1435     my %escapes = ();
1436     for (0..255) {
1437             $escapes{chr($_)} = sprintf("%%%02X", $_);
1438     }
1440     # Default unsafe characters.  RFC 2732 ^(uric - reserved)
1441     $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1443     return $text;
1446 sub OutputSymbolExtraLinks {
1447     my ($symbol) = @_;
1448     my $desc = "";
1450     if (0) { # NEW FEATURE: needs configurability
1451     my $sstr = &uri_escape($symbol);
1452     my $mstr = &uri_escape($MODULE);
1453     $desc .= <<EOF;
1454 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1455 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$sstr">edit documentation</ulink>
1457     }
1458     return $desc;
1461 sub OutputSectionExtraLinks {
1462     my ($symbol,$docsymbol) = @_;
1463     my $desc = "";
1465     if (0) { # NEW FEATURE: needs configurability
1466     my $sstr = &uri_escape($symbol);
1467     my $mstr = &uri_escape($MODULE);
1468     my $dsstr = &uri_escape($docsymbol);
1469     $desc .= <<EOF;
1470 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1471 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$dsstr">edit documentation</ulink>
1473     }
1474     return $desc;
1478 #############################################################################
1479 # Function    : OutputMacro
1480 # Description : Returns the synopsis and detailed description of a macro.
1481 # Arguments   : $symbol - the macro.
1482 #                $declaration - the declaration of the macro.
1483 #############################################################################
1485 sub OutputMacro {
1486     my ($symbol, $declaration) = @_;
1487     my $id = &CreateValidSGMLID ($symbol);
1488     my $condition = &MakeConditionDescription ($symbol);
1489     my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1490     my $desc;
1492     my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1493     my $title = $symbol . (@fields ? "()" : "");
1495     $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1496     $desc .= MakeIndexterms($symbol, $id);
1497     $desc .= "\n";
1498     $desc .= OutputSymbolExtraLinks($symbol);
1500     if (@fields) {
1501         $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1502     }
1503     $synop .= "</entry></row>\n";
1505     # Don't output the macro definition if is is a conditional macro or it
1506     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1507     # longer than 2 lines, otherwise we get lots of complicated macros like
1508     # g_assert.
1509     if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1510         && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1511         my $decl_out = &CreateValidSGML ($declaration);
1512         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1513     } else {
1514         $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1515         if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1516             my $args = $1;
1517             my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1518             # Align each line so that if should all line up OK.
1519             $args =~ s/\n/\n$pad/gm;
1520             $desc .= &CreateValidSGML ($args);
1521         }
1522         $desc .= "</programlisting>\n";
1523     }
1525     $desc .= &MakeDeprecationNote($symbol);
1527     my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1529     if (defined ($SymbolDocs{$symbol})) {
1530         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1531         $desc .= $symbol_docs;
1532     }
1534     $desc .= $parameters;
1535     $desc .= OutputSymbolTraits ($symbol);
1536     $desc .= "</refsect2>\n";
1537     return ($synop, $desc);
1541 #############################################################################
1542 # Function    : OutputTypedef
1543 # Description : Returns the synopsis and detailed description of a typedef.
1544 # Arguments   : $symbol - the typedef.
1545 #                $declaration - the declaration of the typedef,
1546 #                  e.g. 'typedef unsigned int guint;'
1547 #############################################################################
1549 sub OutputTypedef {
1550     my ($symbol, $declaration) = @_;
1551     my $id = &CreateValidSGMLID ($symbol);
1552     my $condition = &MakeConditionDescription ($symbol);
1553     my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1554     my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1556     $desc .= MakeIndexterms($symbol, $id);
1557     $desc .= "\n";
1558     $desc .= OutputSymbolExtraLinks($symbol);
1560     if (!defined ($DeclarationConditional{$symbol})) {
1561         my $decl_out = &CreateValidSGML ($declaration);
1562         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1563     }
1565     $desc .= &MakeDeprecationNote($symbol);
1567     if (defined ($SymbolDocs{$symbol})) {
1568         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1569     }
1570     $desc .= OutputSymbolTraits ($symbol);
1571     $desc .= "</refsect2>\n";
1572     return ($synop, $desc);
1576 #############################################################################
1577 # Function    : OutputStruct
1578 # Description : Returns the synopsis and detailed description of a struct.
1579 #                We check if it is a object struct, and if so we only output
1580 #                parts of it that are noted as public fields.
1581 #                We also use a different SGML ID for object structs, since the
1582 #                original ID is used for the entire RefEntry.
1583 # Arguments   : $symbol - the struct.
1584 #                $declaration - the declaration of the struct.
1585 #############################################################################
1587 sub OutputStruct {
1588     my ($symbol, $declaration) = @_;
1590     my $is_gtype = 0;
1591     my $default_to_public = 1;
1592     if (&CheckIsObject ($symbol)) {
1593         @TRACE@("Found struct gtype: $symbol\n");
1594         $is_gtype = 1;
1595         $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1596     }
1598     my $id;
1599     my $condition;
1600     if ($is_gtype) {
1601         $id = &CreateValidSGMLID ($symbol . "_struct");
1602         $condition = &MakeConditionDescription ($symbol . "_struct");
1603     } else {
1604         $id = &CreateValidSGMLID ($symbol);
1605         $condition = &MakeConditionDescription ($symbol);
1606     }
1608     # Determine if it is a simple struct or it also has a typedef.
1609     my $has_typedef = 0;
1610     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1611       $has_typedef = 1;
1612     }
1614     my $type_output;
1615     my $desc;
1616     if ($has_typedef) {
1617         # For structs with typedefs we just output the struct name.
1618         $type_output = "";
1619         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1620     } else {
1621         $type_output = "struct";
1622         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1623     }
1624     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1626     $desc .= MakeIndexterms($symbol, $id);
1627     $desc .= "\n";
1628     $desc .= OutputSymbolExtraLinks($symbol);
1630     # Form a pretty-printed, private-data-removed form of the declaration
1632     my $decl_out = "";
1633     if ($declaration =~ m/^\s*$/) {
1634         @TRACE@("Found opaque struct: $symbol\n");
1635         $decl_out = "typedef struct _$symbol $symbol;";
1636     } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1637         @TRACE@("Found opaque struct: $symbol\n");
1638         $decl_out = "struct $symbol;";
1639     } else {
1640         my $public = $default_to_public;
1641         my $new_declaration = "";
1642         my $decl_line;
1643         my $decl = $declaration;
1645         if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1646             my $struct_contents = $2;
1648             foreach $decl_line (split (/\n/, $struct_contents)) {
1649                 @TRACE@("Struct line: $decl_line\n");
1650                 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1651                     $public = 1;
1652                 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1653                     $public = 0;
1654                 } elsif ($public) {
1655                     $new_declaration .= $decl_line . "\n";
1656                 }
1657             }
1659             if ($new_declaration) {
1660                 # Strip any blank lines off the ends.
1661                 $new_declaration =~ s/^\s*\n//;
1662                 $new_declaration =~ s/\n\s*$/\n/;
1664                 if ($has_typedef) {
1665                     $decl_out = "typedef struct {\n" . $new_declaration
1666                       . "} $symbol;\n";
1667                 } else {
1668                     $decl_out = "struct $symbol {\n" . $new_declaration
1669                       . "};\n";
1670                 }
1671             }
1672         } else {
1673             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1674                 "Couldn't parse struct:\n$declaration");
1675         }
1677         # If we couldn't parse the struct or it was all private, output an
1678         # empty struct declaration.
1679         if ($decl_out eq "") {
1680             if ($has_typedef) {
1681                 $decl_out = "typedef struct _$symbol $symbol;";
1682             } else {
1683                 $decl_out = "struct $symbol;";
1684             }
1685         }
1686     }
1688     $decl_out = &CreateValidSGML ($decl_out);
1689     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1691     $desc .= &MakeDeprecationNote($symbol);
1693     if (defined ($SymbolDocs{$symbol})) {
1694         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1695     }
1697     # Create a table of fields and descriptions
1699     # FIXME: Inserting &#160's into the produced type declarations here would
1700     #        improve the output in most situations ... except for function
1701     #        members of structs!
1702     my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1703                                         0, \&MakeXRef,
1704                                         sub {
1705                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1706                                         });
1707     my $params = $SymbolParams{$symbol};
1709     # If no parameters are filled in, we don't generate the description
1710     # table, for backwards compatibility.
1712     my $found = 0;
1713     if (defined $params) {
1714         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1715             if ($params->[$i] =~ /\S/) {
1716                 $found = 1;
1717                 last;
1718             }
1719         }
1720     }
1722     if ($found) {
1723         my %field_descrs = @$params;
1724         my $missing_parameters = "";
1725         my $unused_parameters = "";
1727         $desc .= <<EOF;
1728 <refsect3 role="struct_members">\n<title>Members</title>
1729 <informaltable role="struct_members_table" pgwide="1" frame="none">
1730 <tgroup cols="3">
1731 <colspec colname="struct_members_name" colwidth="300px"/>
1732 <colspec colname="struct_members_description"/>
1733 <colspec colname="struct_members_annotations" colwidth="200px"/>
1734 <tbody>
1737         while (@fields) {
1738             my $field_name = shift @fields;
1739             my $text = shift @fields;
1740             my $field_descr = $field_descrs{$field_name};
1741             my $param_annotations = "";
1743             $desc .= "<row><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1744             if (defined $field_descr) {
1745                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1746                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1747                 # trim
1748                 $field_descr =~ s/^(\s|\n)+//msg;
1749                 $field_descr =~ s/(\s|\n)+$//msg;
1750                 $desc .= "<listitem>$field_descr</listitem>\n";
1751                 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1752                 delete $field_descrs{$field_name};
1753             } else {
1754                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1755                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1756                 if ($missing_parameters ne "") {
1757                   $missing_parameters .= ", ".$field_name;
1758                 } else {
1759                     $missing_parameters = $field_name;
1760                 }
1761                 $desc .= "<entry /><entry />\n";
1762             }
1763             $desc .= "</row>\n";
1764         }
1765         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1766         foreach my $field_name (keys %field_descrs) {
1767             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1768                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1769             if ($unused_parameters ne "") {
1770               $unused_parameters .= ", ".$field_name;
1771             } else {
1772                $unused_parameters = $field_name;
1773             }
1774         }
1776         # remember missing/unused parameters (needed in tmpl-free build)
1777         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1778             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1779         }
1780         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1781             $AllUnusedSymbols{$symbol}=$unused_parameters;
1782         }
1783     }
1784     else {
1785         if (scalar(@fields) > 0) {
1786             if (! exists ($AllIncompleteSymbols{$symbol})) {
1787                 $AllIncompleteSymbols{$symbol}="<items>";
1788                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1789                     "Field descriptions for struct $symbol are missing in source code comment block.");
1790                 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1791             }
1792         }
1793     }
1795     $desc .= OutputSymbolTraits ($symbol);
1796     $desc .= "</refsect2>\n";
1797     return ($synop, $desc);
1801 #############################################################################
1802 # Function    : OutputUnion
1803 # Description : Returns the synopsis and detailed description of a union.
1804 # Arguments   : $symbol - the union.
1805 #                $declaration - the declaration of the union.
1806 #############################################################################
1808 sub OutputUnion {
1809     my ($symbol, $declaration) = @_;
1811     my $is_gtype = 0;
1812     if (&CheckIsObject ($symbol)) {
1813         @TRACE@("Found union gtype: $symbol\n");
1814         $is_gtype = 1;
1815     }
1817     my $id;
1818     my $condition;
1819     if ($is_gtype) {
1820         $id = &CreateValidSGMLID ($symbol . "_union");
1821         $condition = &MakeConditionDescription ($symbol . "_union");
1822     } else {
1823         $id = &CreateValidSGMLID ($symbol);
1824         $condition = &MakeConditionDescription ($symbol);
1825     }
1827     # Determine if it is a simple struct or it also has a typedef.
1828     my $has_typedef = 0;
1829     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1830       $has_typedef = 1;
1831     }
1833     my $type_output;
1834     my $desc;
1835     if ($has_typedef) {
1836         # For unions with typedefs we just output the union name.
1837         $type_output = "";
1838         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1839     } else {
1840         $type_output = "union";
1841         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1842     }
1843     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1845     $desc .= MakeIndexterms($symbol, $id);
1846     $desc .= "\n";
1847     $desc .= OutputSymbolExtraLinks($symbol);
1848     $desc .= &MakeDeprecationNote($symbol);
1850     if (defined ($SymbolDocs{$symbol})) {
1851         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1852     }
1854     # Create a table of fields and descriptions
1856     # FIXME: Inserting &#160's into the produced type declarations here would
1857     #        improve the output in most situations ... except for function
1858     #        members of structs!
1859     my @fields = ParseStructDeclaration($declaration, 0,
1860                                         0, \&MakeXRef,
1861                                         sub {
1862                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1863                                         });
1864     my $params = $SymbolParams{$symbol};
1866     # If no parameters are filled in, we don't generate the description
1867     # table, for backwards compatibility
1869     my $found = 0;
1870     if (defined $params) {
1871         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1872             if ($params->[$i] =~ /\S/) {
1873                 $found = 1;
1874                 last;
1875             }
1876         }
1877     }
1879     if ($found) {
1880         my %field_descrs = @$params;
1881         my $missing_parameters = "";
1882         my $unused_parameters = "";
1884         $desc .= <<EOF;
1885 <refsect3 role="union_members">\n<title>Members</title>
1886 <informaltable role="union_members_table" pgwide="1" frame="none">
1887 <tgroup cols="3">
1888 <colspec colname="union_members_name" colwidth="300px"/>
1889 <colspec colname="union_members_description"/>
1890 <colspec colname="union_members_annotations" colwidth="200px"/>
1891 <tbody>
1894         while (@fields) {
1895             my $field_name = shift @fields;
1896             my $text = shift @fields;
1897             my $field_descr = $field_descrs{$field_name};
1898             my $param_annotations = "";
1900             $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1901             if (defined $field_descr) {
1902                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1903                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1905                 # trim
1906                 $field_descr =~ s/^(\s|\n)+//msg;
1907                 $field_descr =~ s/(\s|\n)+$//msg;
1908                 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1909                 delete $field_descrs{$field_name};
1910             } else {
1911                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1912                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1913                 if ($missing_parameters ne "") {
1914                     $missing_parameters .= ", ".$field_name;
1915                 } else {
1916                     $missing_parameters = $field_name;
1917                 }
1918                 $desc .= "<entry /><entry />\n";
1919             }
1920             $desc .= "</row>\n";
1921         }
1922         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1923         foreach my $field_name (keys %field_descrs) {
1924             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1925                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1926             if ($unused_parameters ne "") {
1927               $unused_parameters .= ", ".$field_name;
1928             } else {
1929                $unused_parameters = $field_name;
1930             }
1931         }
1933         # remember missing/unused parameters (needed in tmpl-free build)
1934         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1935             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1936         }
1937         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1938             $AllUnusedSymbols{$symbol}=$unused_parameters;
1939         }
1940     }
1941     else {
1942         if (scalar(@fields) > 0) {
1943             if (! exists ($AllIncompleteSymbols{$symbol})) {
1944                 $AllIncompleteSymbols{$symbol}="<items>";
1945                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1946                     "Field descriptions for union $symbol are missing in source code comment block.");
1947                 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1948             }
1949         }
1950     }
1952     $desc .= OutputSymbolTraits ($symbol);
1953     $desc .= "</refsect2>\n";
1954     return ($synop, $desc);
1958 #############################################################################
1959 # Function    : OutputEnum
1960 # Description : Returns the synopsis and detailed description of a enum.
1961 # Arguments   : $symbol - the enum.
1962 #                $declaration - the declaration of the enum.
1963 #############################################################################
1965 sub OutputEnum {
1966     my ($symbol, $declaration) = @_;
1968     my $is_gtype = 0;
1969     if (&CheckIsObject ($symbol)) {
1970         @TRACE@("Found enum gtype: $symbol\n");
1971         $is_gtype = 1;
1972     }
1974     my $id;
1975     my $condition;
1976     if ($is_gtype) {
1977         $id = &CreateValidSGMLID ($symbol . "_enum");
1978         $condition = &MakeConditionDescription ($symbol . "_enum");
1979     } else {
1980         $id = &CreateValidSGMLID ($symbol);
1981         $condition = &MakeConditionDescription ($symbol);
1982     }
1984     my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1985     my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1987     $desc .= MakeIndexterms($symbol, $id);
1988     $desc .= "\n";
1989     $desc .= OutputSymbolExtraLinks($symbol);
1990     $desc .= &MakeDeprecationNote($symbol);
1992     if (defined ($SymbolDocs{$symbol})) {
1993         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1994     }
1996     # Create a table of fields and descriptions
1998     my @fields = ParseEnumDeclaration($declaration);
1999     my $params = $SymbolParams{$symbol};
2001     # If nothing at all is documented log a single summary warning at the end.
2002     # Otherwise, warn about each undocumented item.
2004     my $found = 0;
2005     if (defined $params) {
2006         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
2007             if ($params->[$i] =~ /\S/) {
2008                 $found = 1;
2009                 last;
2010             }
2011         }
2012     }
2014     my %field_descrs = (defined $params ? @$params : ());
2015     my $missing_parameters = "";
2016     my $unused_parameters = "";
2018     $desc .= <<EOF;
2019 <refsect3 role="enum_members">\n<title>Members</title>
2020 <informaltable role="enum_members_table" pgwide="1" frame="none">
2021 <tgroup cols="3">
2022 <colspec colname="enum_members_name" colwidth="300px"/>
2023 <colspec colname="enum_members_description"/>
2024 <colspec colname="enum_members_annotations" colwidth="200px"/>
2025 <tbody>
2028     for my $field_name (@fields) {
2029         my $field_descr = $field_descrs{$field_name};
2030         my $param_annotations = "";
2032         $id = &CreateValidSGMLID ($field_name);
2033         $condition = &MakeConditionDescription ($field_name);
2034         $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2035         if (defined $field_descr) {
2036             ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2037             $field_descr = &ConvertMarkDown($symbol, $field_descr);
2038             $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2039             delete $field_descrs{$field_name};
2040         } else {
2041             if ($found) {
2042                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2043                     "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2044                 if ($missing_parameters ne "") {
2045                     $missing_parameters .= ", ".$field_name;
2046                 } else {
2047                     $missing_parameters = $field_name;
2048                 }
2049             }
2050             $desc .= "<entry /><entry />\n";
2051         }
2052         $desc .= "</row>\n";
2053     }
2054     $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2055     foreach my $field_name (keys %field_descrs) {
2056         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2057             "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2058         if ($unused_parameters ne "") {
2059             $unused_parameters .= ", ".$field_name;
2060         } else {
2061             $unused_parameters = $field_name;
2062         }
2063     }
2065     # remember missing/unused parameters (needed in tmpl-free build)
2066     if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2067         $AllIncompleteSymbols{$symbol}=$missing_parameters;
2068     }
2069     if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2070         $AllUnusedSymbols{$symbol}=$unused_parameters;
2071     }
2073     if (!$found) {
2074         if (scalar(@fields) > 0) {
2075             if (! exists ($AllIncompleteSymbols{$symbol})) {
2076                 $AllIncompleteSymbols{$symbol}="<items>";
2077                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2078                     "Value descriptions for $symbol are missing in source code comment block.");
2079             }
2080         }
2081     }
2083     $desc .= OutputSymbolTraits ($symbol);
2084     $desc .= "</refsect2>\n";
2085     return ($synop, $desc);
2089 #############################################################################
2090 # Function    : OutputVariable
2091 # Description : Returns the synopsis and detailed description of a variable.
2092 # Arguments   : $symbol - the extern'ed variable.
2093 #                $declaration - the declaration of the variable.
2094 #############################################################################
2096 sub OutputVariable {
2097     my ($symbol, $declaration) = @_;
2098     my $id = &CreateValidSGMLID ($symbol);
2099     my $condition = &MakeConditionDescription ($symbol);
2100     
2101     @TRACE@("ouputing variable: '$symbol' '$declaration'");
2103     my $type_output;
2104     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*;/) {
2105         my $mod1 = defined ($1) ? $1 : "";
2106         my $ptr = defined ($3) ? $3 : "";
2107         my $space = defined ($4) ? $4 : "";
2108         my $mod2 = defined ($5) ? $5 : "";
2109         $type_output = "extern $mod1$ptr$space$mod2";
2110     } 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*=/) {
2111         my $mod1 = defined ($1) ? $1 : "";
2112         my $ptr = defined ($3) ? $3 : "";
2113         my $space = defined ($4) ? $4 : "";
2114         my $mod2 = defined ($5) ? $5 : "";
2115         $type_output = "$mod1$ptr$space$mod2";
2116     } else {
2117         $type_output = "extern";
2118     }
2119     my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2121     my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2123     $desc .= MakeIndexterms($symbol, $id);
2124     $desc .= "\n";
2125     $desc .= OutputSymbolExtraLinks($symbol);
2127     my $decl_out = &CreateValidSGML ($declaration);
2128     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2130     $desc .= &MakeDeprecationNote($symbol);
2132     if (defined ($SymbolDocs{$symbol})) {
2133         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2134     }
2135     $desc .= OutputSymbolTraits ($symbol);
2136     $desc .= "</refsect2>\n";
2137     return ($synop, $desc);
2141 #############################################################################
2142 # Function    : OutputFunction
2143 # Description : Returns the synopsis and detailed description of a function.
2144 # Arguments   : $symbol - the function.
2145 #                $declaration - the declaration of the function.
2146 #############################################################################
2148 sub OutputFunction {
2149     my ($symbol, $declaration, $symbol_type) = @_;
2150     my $id = &CreateValidSGMLID ($symbol);
2151     my $condition = &MakeConditionDescription ($symbol);
2153     # Take out the return type     $1                                                                                       $2   $3
2154     $declaration =~ s/<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n//;
2155     my $type_modifier = defined($1) ? $1 : "";
2156     my $type = $2;
2157     my $pointer = $3;
2158     # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2159     $pointer =~ s/\s+$//;
2160     my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2161     my $start = "";
2162     #if ($symbol_type eq 'USER_FUNCTION') {
2163     #    $start = "typedef ";
2164     #}
2166     # We output const rather than G_CONST_RETURN.
2167     $type_modifier =~ s/G_CONST_RETURN/const/g;
2168     $pointer =~ s/G_CONST_RETURN/const/g;
2169     $pointer =~ s/^\s+/&#160;/g;
2171     my $ret_type_output;
2172     $ret_type_output = "$start$type_modifier$xref$pointer\n";
2174     my $indent_len;
2175     $indent_len = length ($symbol) + 2;
2176     my $char1 = my $char2 = my $char3 = "";
2177     if ($symbol_type eq 'USER_FUNCTION') {
2178         $indent_len += 3;
2179         $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2180         $char2 = "*";
2181         $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2182     }
2184     my ($symbol_output, $symbol_desc_output);
2185     $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2186     if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2187         $symbol_desc_output = "$char1$char2$symbol$char3 ";
2188     } else {
2189         $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2190         $symbol_desc_output = "$char1$char2$symbol$char3\n"
2191           . (' ' x ($indent_len - 1));
2192     }
2194     my $synop = "<row><entry role=\"function_type\">${ret_type_output}</entry><entry role=\"function_name\">${symbol_output}&#160;<phrase role=\"c_punctuation\">()</phrase></entry></row>\n";
2196     my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol}&#160;()</title>\n";
2198     $desc .= MakeIndexterms($symbol, $id);
2199     $desc .= "\n";
2200     $desc .= OutputSymbolExtraLinks($symbol);
2202     $desc  .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2204     my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2205                                         sub {
2206                                             &tagify($_[0],"parameter");
2207                                         });
2209     for (my $i = 1; $i <= $#fields; $i += 2) {
2210         my $field_name = $fields[$i];
2212         if ($i == 1) {
2213             $desc  .= "$field_name";
2214         } else {
2215             $desc  .= ",\n"
2216                 . (' ' x $indent_len)
2217                 . "$field_name";
2218         }
2220     }
2222     $desc  .= ");</programlisting>\n";
2224     $desc .= &MakeDeprecationNote($symbol);
2226     my $parameters = &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2228     if (defined ($SymbolDocs{$symbol})) {
2229         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2230         $desc .= $symbol_docs;
2231     }
2233     $desc .= $parameters;
2234     $desc .= OutputSymbolTraits ($symbol);
2235     $desc .= "</refsect2>\n";
2236     return ($synop, $desc);
2240 #############################################################################
2241 # Function    : OutputParamDescriptions
2242 # Description : Returns the DocBook output describing the parameters of a
2243 #                function, macro or signal handler.
2244 # Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2245 #                  handlers have an implicit user_data parameter last.
2246 #                $symbol - the name of the function/macro being described.
2247 #               @fields - parsed fields from the declaration, used to determine
2248 #                  undocumented/unused entries
2249 #############################################################################
2251 sub OutputParamDescriptions {
2252     my ($symbol_type, $symbol, @fields) = @_;
2253     my $output = "";
2254     my $params = $SymbolParams{$symbol};
2255     my $num_params = 0;
2256     my %field_descrs = ();
2258     if (@fields) {
2259         %field_descrs = @fields;
2260         delete $field_descrs{"void"};
2261         delete $field_descrs{"Returns"};
2262     }
2264     if (defined $params) {
2265         my $returns = "";
2266         my $params_desc = "";
2267         my $missing_parameters = "";
2268         my $unused_parameters = "";
2269         my $j;
2271         for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2272             my $param_name = $$params[$j];
2273             my $param_desc = $$params[$j + 1];
2274             my $param_annotations = "";
2276             ($param_desc,$param_annotations) = & ExpandAnnotation($symbol, $param_desc);
2277             $param_desc = &ConvertMarkDown($symbol, $param_desc);
2278             # trim
2279             $param_desc =~ s/^(\s|\n)+//msg;
2280             $param_desc =~ s/(\s|\n)+$//msg;
2281             if ($param_name eq "Returns") {
2282                 $returns = "$param_desc\n<para>$param_annotations</para>";
2283             } elsif ($param_name eq "void") {
2284                 # FIXME: &LogWarning()?
2285                 @TRACE@("!!!! void in params for $symbol?\n");
2286             } else {
2287                 if (@fields) {
2288                     if (!defined $field_descrs{$param_name}) {
2289                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2290                             "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2291                         if ($unused_parameters ne "") {
2292                           $unused_parameters .= ", ".$param_name;
2293                         } else {
2294                            $unused_parameters = $param_name;
2295                         }
2296                     } else {
2297                         delete $field_descrs{$param_name};
2298                     }
2299                 }
2300                 if($param_desc ne "") {
2301                     $params_desc .= "<row><entry role=\"parameter_name\"><para>$param_name</para></entry>\n<entry role=\"parameter_description\">$param_desc</entry>\n<entry role=\"parameter_annotations\">$param_annotations</entry></row>\n";
2302                     $num_params++;
2303                 }
2304             }
2305         }
2306         foreach my $param_name (keys %field_descrs) {
2307             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2308                 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2309             if ($missing_parameters ne "") {
2310               $missing_parameters .= ", ".$param_name;
2311             } else {
2312                $missing_parameters = $param_name;
2313             }
2314         }
2316         # Signals have an implicit user_data parameter which we describe.
2317         if ($symbol_type eq "SIGNAL") {
2318             $params_desc .= "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry role=\"parameter_description\"><simpara>user data set when the signal handler was connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n";
2319         }
2321         # Start a table if we need one.
2322         if ($params_desc ne "") {
2323           $output .= <<EOF;
2324 <refsect3 role="parameters">\n<title>Parameters</title>
2325 <informaltable role="parameters_table" pgwide="1" frame="none">
2326 <tgroup cols="3">
2327 <colspec colname="parameters_name" colwidth="150px"/>
2328 <colspec colname="parameters_description"/>
2329 <colspec colname="parameters_annotations" colwidth="200px"/>
2330 <tbody>
2332           $output .= $params_desc;
2333           $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2334         }
2336         # Output the returns info last
2337         if ($returns ne "") {
2338           $output .= <<EOF;
2339 <refsect3 role=\"returns\">\n<title>Returns</title>
2341           $output .= $returns;
2342           $output .= "\n</refsect3>";
2343         }
2345         # remember missing/unused parameters (needed in tmpl-free build)
2346         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2347             $AllIncompleteSymbols{$symbol}=$missing_parameters;
2348         }
2349         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2350             $AllUnusedSymbols{$symbol}=$unused_parameters;
2351         }
2352     }
2353     if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2354         if (! exists ($AllIncompleteSymbols{$symbol})) {
2355             $AllIncompleteSymbols{$symbol}="<parameters>";
2356         }
2357     }
2359     return $output;
2363 #############################################################################
2364 # Function    : ParseStabilityLevel
2365 # Description : Parses a stability level and outputs a warning if it isn't
2366 #               valid.
2367 # Arguments   : $stability - the stability text.
2368 #                $file, $line - context for error message
2369 #                $message - description of where the level is from, to use in
2370 #               any error message.
2371 # Returns     : The parsed stability level string.
2372 #############################################################################
2374 sub ParseStabilityLevel {
2375     my ($stability, $file, $line, $message) = @_;
2377     $stability =~ s/^\s*//;
2378     $stability =~ s/\s*$//;
2379     if ($stability =~ m/^stable$/i) {
2380         $stability = "Stable";
2381     } elsif ($stability =~ m/^unstable$/i) {
2382         $stability = "Unstable";
2383     } elsif ($stability =~ m/^private$/i) {
2384         $stability = "Private";
2385     } else {
2386         &LogWarning ($file, $line, "$message is $stability.".
2387             "It should be one of these: Stable, Unstable, or Private.");
2388     }
2389     return $stability;
2393 #############################################################################
2394 # Function    : OutputSGMLFile
2395 # Description : Outputs the final DocBook file for one section.
2396 # Arguments   : $file - the name of the file.
2397 #               $title - the title from the $MODULE-sections.txt file, which
2398 #                 will be overridden by the title in the template file.
2399 #               $section_id - the SGML id to use for the toplevel tag.
2400 #               $includes - comma-separates list of include files added at top of
2401 #                 synopsis, with '<' '>' around them (if not already enclosed in "").
2402 #               $functions_synop - reference to the DocBook for the Functions Synopsis part.
2403 #               $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2404 #               $functions_details - reference to the DocBook for the Functions Details part.
2405 #               $other_details - reference to the DocBook for the Types and Values Details part.
2406 #               $signal_synop - reference to the DocBook for the Signal Synopsis part
2407 #               $signal_desc - reference to the DocBook for the Signal Description part
2408 #               $args_synop - reference to the DocBook for the Arg Synopsis part
2409 #               $args_desc - reference to the DocBook for the Arg Description part
2410 #               $hierarchy - reference to the DocBook for the Object Hierarchy part
2411 #               $interfaces - reference to the DocBook for the Interfaces part
2412 #               $implementations - reference to the DocBook for the Known Implementations part
2413 #               $prerequisites - reference to the DocBook for the Prerequisites part
2414 #               $derived - reference to the DocBook for the Derived Interfaces part
2415 #               $file_objects - reference to an array of objects in this file
2416 #############################################################################
2418 sub OutputSGMLFile {
2419     my ($file, $title, $section_id, $includes, $functions_synop, $other_synop, $functions_details, $other_details, $signals_synop, $signals_desc, $args_synop, $args_desc, $hierarchy, $interfaces, $implementations, $prerequisites, $derived, $file_objects) = @_;
2421     @TRACE@("Output sgml for file $file with title '$title'\n");
2423     # The edited title overrides the one from the sections file.
2424     my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2425     if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2426         $title = $new_title;
2427         @TRACE@("Found title: $title\n");
2428     }
2429     my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2430     if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2431         $short_desc = "";
2432     } else {
2433         # Don't use ConvertMarkDown here for now since we don't want blocks
2434         $short_desc = &ExpandAbbreviations("$title:Short_description",
2435                                            $short_desc);
2436         @TRACE@("Found short_desc: $short_desc");
2437     }
2438     my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2439     if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2440         $long_desc = "";
2441     } else {
2442         $long_desc = &ConvertMarkDown("$title:Long_description",
2443                                           $long_desc);
2444         @TRACE@("Found long_desc: $long_desc");
2445     }
2446     my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2447     if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2448         $see_also = "";
2449     } else {
2450         $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2451         @TRACE@("Found see_also: $see_also");
2452     }
2453     if ($see_also) {
2454         $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2455     }
2456     my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2457     if (!defined ($stability) || $stability =~ m/^\s*$/) {
2458         $stability = "";
2459     } else {
2460         $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2461         @TRACE@("Found stability: $stability");
2462     }
2463     if ($stability) {
2464         $AnnotationsUsed{$stability} = 1;
2465         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2466     } elsif ($DEFAULT_STABILITY) {
2467         $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2468         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2469     }
2471     my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2472     if (!defined ($image) || $image =~ m/^\s*$/) {
2473       $image = "";
2474     } else {
2475       $image =~ s/^\s*//;
2476       $image =~ s/\s*$//;
2478       my $format;
2480       if ($image =~ /jpe?g$/i) {
2481         $format = "format='JPEG'";
2482       } elsif ($image =~ /png$/i) {
2483         $format = "format='PNG'";
2484       } elsif ($image =~ /svg$/i) {
2485         $format = "format='SVG'";
2486       } else {
2487         $format = "";
2488       }
2490       $image = "  <inlinegraphic fileref='$image' $format/>\n"
2491     }
2493     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2494         gmtime (time);
2495     my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2496     $year += 1900;
2498     my $include_output = "";
2499     if ($includes) {
2500       $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2501       my $include;
2502       foreach $include (split (/,/, $includes)) {
2503         if ($include =~ m/^\".+\"$/) {
2504           $include_output .= "#include ${include}\n";
2505         }
2506         else {
2507           $include =~ s/^\s+|\s+$//gs;
2508           $include_output .= "#include &lt;${include}&gt;\n";
2509         }
2510       }
2511       $include_output .= "</synopsis></refsect1>\n";
2512     }
2514     my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2516     my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT";
2517     my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new";
2519     open (OUTPUT, ">$new_sgml_file")
2520         || die "Can't create $new_sgml_file: $!";
2522     my $object_anchors = "";
2523     foreach my $object (@$file_objects) {
2524         next if ($object eq $section_id);
2525         my $id = CreateValidSGMLID($object);
2526         @TRACE@("Adding anchor for $object\n");
2527         $object_anchors .= "<anchor id=\"$id\"$empty_element_end";
2528     }
2530     # We used to output this, but is messes up our UpdateFileIfChanged code
2531     # since it changes every day (and it is only used in the man pages):
2532     # "<refentry id="$section_id" revision="$mday $month $year">"
2534     if ($OUTPUT_FORMAT eq "xml") {
2535         print OUTPUT $doctype_header;
2536     }
2538     print OUTPUT <<EOF;
2539 <refentry id="$section_id">
2540 <refmeta>
2541 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2542 <manvolnum>3</manvolnum>
2543 <refmiscinfo>
2544   \U$MODULE\E Library
2545 $image</refmiscinfo>
2546 </refmeta>
2547 <refnamediv>
2548 <refname>$title</refname>
2549 <refpurpose>$short_desc</refpurpose>
2550 </refnamediv>
2551 $stability
2552 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2553 $include_output
2554 <refsect1 id="$section_id.description" role="desc">
2555 <title role="desc.title">Description</title>
2556 $extralinks$long_desc
2557 </refsect1>
2558 <refsect1 id="$section_id.functions_details" role="details">
2559 <title role="details.title">Functions</title>
2560 $$functions_details
2561 </refsect1>
2562 <refsect1 id="$section_id.other_details" role="details">
2563 <title role="details.title">Types and Values</title>
2564 $$other_details
2565 </refsect1>
2566 $$args_desc$$signals_desc$see_also
2567 </refentry>
2569     close (OUTPUT);
2571     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2575 #############################################################################
2576 # Function    : OutputExtraFile
2577 # Description : Copies an "extra" DocBook file into the output directory,
2578 #               expanding abbreviations
2579 # Arguments   : $file - the source file.
2580 #############################################################################
2581 sub OutputExtraFile {
2582     my ($file) = @_;
2584     my $basename;
2586     ($basename = $file) =~ s!^.*/!!;
2588     my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename";
2589     my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new";
2591     my $contents;
2593     open(EXTRA_FILE, "<$file") || die "Can't open $file";
2595     {
2596         local $/;
2597         $contents = <EXTRA_FILE>;
2598     }
2600     open (OUTPUT, ">$new_sgml_file")
2601         || die "Can't create $new_sgml_file: $!";
2603     print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2604     close (OUTPUT);
2606     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2608 #############################################################################
2609 # Function    : OutputBook
2610 # Description : Outputs the SGML entities that need to be included into the
2611 #                main SGML file for the module.
2612 # Arguments   : $book_top - the declarations of the entities, which are added
2613 #                  at the top of the main SGML file.
2614 #                $book_bottom - the references to the entities, which are
2615 #                  added in the main SGML file at the desired position.
2616 #############################################################################
2618 sub OutputBook {
2619     my ($book_top, $book_bottom) = @_;
2621     my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top";
2622     my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new";
2624     open (OUTPUT, ">$new_file")
2625         || die "Can't create $new_file: $!";
2626     print OUTPUT $book_top;
2627     close (OUTPUT);
2629     &UpdateFileIfChanged ($old_file, $new_file, 0);
2632     $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom";
2633     $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new";
2635     open (OUTPUT, ">$new_file")
2636         || die "Can't create $new_file: $!";
2637     print OUTPUT $book_bottom;
2638     close (OUTPUT);
2640     &UpdateFileIfChanged ($old_file, $new_file, 0);
2643     # If the main SGML/XML file hasn't been created yet, we create it here.
2644     # The user can tweak it later.
2645     if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2646       open (OUTPUT, ">$MAIN_SGML_FILE")
2647         || die "Can't create $MAIN_SGML_FILE: $!";
2649       if ($OUTPUT_FORMAT eq "xml") {
2650           print OUTPUT <<EOF;
2651 <?xml version="1.0"?>
2652 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2653                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2655   <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
2657 <book id="index">
2659       } else {
2660         print OUTPUT <<EOF;
2661 <!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
2662 $book_top
2664 <book id="index">
2666       }
2668 print OUTPUT <<EOF;
2669   <bookinfo>
2670     <title>$MODULE Reference Manual</title>
2671     <releaseinfo>
2672       for $MODULE [VERSION].
2673       The latest version of this documentation can be found on-line at
2674       <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2675     </releaseinfo>
2676   </bookinfo>
2678   <chapter>
2679     <title>[Insert title here]</title>
2680     $book_bottom
2681   </chapter>
2683   if (-e $OBJECT_TREE_FILE) {
2684     print OUTPUT <<EOF;
2685   <chapter id="object-tree">
2686     <title>Object Hierarchy</title>
2687      <xi:include href="xml/tree_index.sgml"/>
2688   </chapter>
2690   }
2692 print OUTPUT <<EOF;
2693   <index id="api-index-full">
2694     <title>API Index</title>
2695     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2696   </index>
2697   <index id="deprecated-api-index" role="deprecated">
2698     <title>Index of deprecated API</title>
2699     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2700   </index>
2702   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2703 </book>
2706       close (OUTPUT);
2707     }
2711 #############################################################################
2712 # Function    : CreateValidSGML
2713 # Description : This turns any chars which are used in SGML into entities,
2714 #                e.g. '<' into '&lt;'
2715 # Arguments   : $text - the text to turn into proper SGML.
2716 #############################################################################
2718 sub CreateValidSGML {
2719     my ($text) = @_;
2720     $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
2721     $text =~ s/</&lt;/g;
2722     $text =~ s/>/&gt;/g;
2723     # browers render single tabs inconsistently
2724     $text =~ s/([^\s])\t([^\s])/$1&#160;$2/g;
2725     return $text;
2728 #############################################################################
2729 # Function    : ConvertSGMLChars
2730 # Description : This is used for text in source code comment blocks, to turn
2731 #               chars which are used in SGML into entities, e.g. '<' into
2732 #               '&lt;'. Depending on $INLINE_MARKUP_MODE, this is done
2733 #               unconditionally or only if the character doesn't seem to be
2734 #               part of an SGML construct (tag or entity reference).
2735 # Arguments   : $text - the text to turn into proper SGML.
2736 #############################################################################
2738 sub ConvertSGMLChars {
2739     my ($symbol, $text) = @_;
2741     if ($INLINE_MARKUP_MODE) {
2742         # For the XML/SGML mode only convert to entities outside CDATA sections.
2743         return &ModifyXMLElements ($text, $symbol,
2744                                    "<!\\[CDATA\\[|<programlisting[^>]*>",
2745                                    \&ConvertSGMLCharsEndTag,
2746                                    \&ConvertSGMLCharsCallback);
2747     } else {
2748         # For the simple non-sgml mode, convert to entities everywhere.
2750         # First, convert freestanding & to &amp;
2751         $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;
2752         $text =~ s/</&lt;/g;
2753         # Allow ">" at beginning of string for blockquote markdown
2754         $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2756         return $text;
2757     }
2761 sub ConvertSGMLCharsEndTag {
2762   if ($_[0] eq "<!\[CDATA\[") {
2763     return "]]>";
2764   } else {
2765     return "</programlisting>";
2766   }
2769 sub ConvertSGMLCharsCallback {
2770   my ($text, $symbol, $tag) = @_;
2772   if ($tag =~ m/^<programlisting/) {
2773     # We can handle <programlisting> specially here.
2774     return &ModifyXMLElements ($text, $symbol,
2775                                "<!\\[CDATA\\[",
2776                                \&ConvertSGMLCharsEndTag,
2777                                \&ConvertSGMLCharsCallback2);
2778   } elsif ($tag eq "") {
2779     # If we're not in CDATA convert to entities.
2780     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2781     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2782     # Allow ">" at beginning of string for blockquote markdown
2783     $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2785     # Handle "#include <xxxxx>"
2786     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2787   }
2789   return $text;
2792 sub ConvertSGMLCharsCallback2 {
2793   my ($text, $symbol, $tag) = @_;
2795   # If we're not in CDATA convert to entities.
2796   # We could handle <programlisting> differently, though I'm not sure it helps.
2797   if ($tag eq "") {
2798     # replace only if its not a tag
2799     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2800     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2801     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
2803     # Handle "#include <xxxxx>"
2804     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2805   }
2807   return $text;
2810 #############################################################################
2811 # Function    : ExpandAnnotation
2812 # Description : This turns annotations into acronym tags.
2813 # Arguments   : $symbol - the symbol being documented, for error messages.
2814 #                $text - the text to expand.
2815 #############################################################################
2816 sub ExpandAnnotation {
2817     my ($symbol, $param_desc) = @_;
2818     my $param_annotations = "";
2820     # look for annotations at the start of the comment part
2821     if ($param_desc =~ m%^\s*\((.*?)\):%) {
2822         my @annotations;
2823         my $annotation;
2824         $param_desc = $';
2826         @annotations = split(/\)\s*\(/,$1);
2827         foreach $annotation (@annotations) {
2828             # need to search for the longest key-match in %AnnotationDefinition
2829             my $match_length=0;
2830             my $match_annotation="";
2831             my $annotationdef;
2832             foreach $annotationdef (keys %AnnotationDefinition) {
2833                 if ($annotation =~ m/^$annotationdef/) {
2834                     if (length($annotationdef)>$match_length) {
2835                         $match_length=length($annotationdef);
2836                         $match_annotation=$annotationdef;
2837                     }
2838                 }
2839             }
2840             my $annotation_extra = "";
2841             if ($match_annotation ne "") {
2842                 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2843                     $annotation_extra = " $1";
2844                 }
2845                 $AnnotationsUsed{$match_annotation} = 1;
2846                 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2847             }
2848             else {
2849                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2850                     "unknown annotation \"$annotation\" in documentation for $symbol.");
2851                 $param_annotations .= "[$annotation]";
2852             }
2853         }
2854         chomp($param_desc);
2855         $param_desc =~ m/^(.*?)\.*\s*$/s;
2856         $param_desc = "$1. ";
2857     }
2858     if ($param_annotations ne "") {
2859         $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2860     }
2861     return ($param_desc, $param_annotations);
2864 #############################################################################
2865 # Function    : ExpandAbbreviations
2866 # Description : This turns the abbreviations function(), macro(), @param,
2867 #                %constant, and #symbol into appropriate DocBook markup.
2868 #               CDATA sections and <programlisting> parts are skipped.
2869 # Arguments   : $symbol - the symbol being documented, for error messages.
2870 #                $text - the text to expand.
2871 #############################################################################
2873 sub ExpandAbbreviations {
2874   my ($symbol, $text) = @_;
2876   # Note: This is a fallback and normally done in the markdown parser
2878   # Convert "|[" and "]|" into the start and end of program listing examples.
2879   # Support \[<!-- language="C" --> modifiers
2880   $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2881   $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2882   $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2884   # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2885   # as such)
2886   return &ModifyXMLElements ($text, $symbol,
2887                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2888                              \&ExpandAbbreviationsEndTag,
2889                              \&ExpandAbbreviationsCallback);
2893 # Returns the end tag (as a regexp) corresponding to the given start tag.
2894 sub ExpandAbbreviationsEndTag {
2895   my ($start_tag) = @_;
2897   if ($start_tag eq "<!\[CDATA\[") {
2898     return "]]>";
2899   } elsif ($start_tag eq "<!DOCTYPE") {
2900     return ">";
2901   } elsif ($start_tag =~ m/<(\w+)/) {
2902     return "</$1>";
2903   }
2906 # Called inside or outside each CDATA or <programlisting> section.
2907 sub ExpandAbbreviationsCallback {
2908   my ($text, $symbol, $tag) = @_;
2910   if ($tag =~ m/^<programlisting/) {
2911     # Handle any embedded CDATA sections.
2912     return &ModifyXMLElements ($text, $symbol,
2913                                "<!\\[CDATA\\[",
2914                                \&ExpandAbbreviationsEndTag,
2915                                \&ExpandAbbreviationsCallback2);
2916   } elsif ($tag eq "") {
2917     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2919     # We are outside any CDATA or <programlisting> sections, so we expand
2920     # any gtk-doc abbreviations.
2922     # Convert '@param()'
2923     # FIXME: we could make those also links ($symbol.$2), but that would be less
2924     # useful as the link target is a few lines up or down
2925     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2927     # Convert 'function()' or 'macro()'.
2928     # if there is abc_*_def() we don't want to make a link to _def()
2929     # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2930     $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2931     # handle #Object.func()
2932     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2934     # Convert '@param', but not '\@param'.
2935     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2936     $text =~ s/\\\@/\@/g;
2938     # Convert '%constant', but not '\%constant'.
2939     # Also allow negative numbers, e.g. %-1.
2940     $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2941     $text =~ s/\\\%/\%/g;
2943     # Convert '#symbol', but not '\#symbol'.
2944     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2945     $text =~ s/\\#/#/g;
2946   }
2948   return $text;
2951 # This is called inside a <programlisting>
2952 sub ExpandAbbreviationsCallback2 {
2953   my ($text, $symbol, $tag) = @_;
2955   if ($tag eq "") {
2956     # We are inside a <programlisting> but outside any CDATA sections,
2957     # so we expand any gtk-doc abbreviations.
2958     # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2959     #        why not just call it
2960     $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2961   } elsif ($tag eq "<![CDATA[") {
2962     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2963     $text = &ReplaceEntities ($text, $symbol);
2964   }
2966   return $text;
2969 sub MakeHashXRef {
2970     my ($symbol, $tag) = @_;;
2971     my $text = $symbol;
2973     # Check for things like '#include', '#define', and skip them.
2974     if ($PreProcessorDirectives{$symbol}) {
2975       return "#$symbol";
2976     }
2978     # Get rid of special suffixes ('-struct','-enum').
2979     $text =~ s/-struct$//;
2980     $text =~ s/-enum$//;
2982     # If the symbol is in the form "Object::signal", then change the symbol to
2983     # "Object-signal" and use "signal" as the text.
2984     if ($symbol =~ s/::/-/) {
2985       $text = "“$'”";
2986     }
2988     # If the symbol is in the form "Object:property", then change the symbol to
2989     # "Object--property" and use "property" as the text.
2990     if ($symbol =~ s/:/--/) {
2991       $text = "“$'”";
2992     }
2994     if ($tag ne "") {
2995       $text = tagify ($text, $tag);
2996     }
2998     return &MakeXRef($symbol, $text);
3002 #############################################################################
3003 # Function    : ModifyXMLElements
3004 # Description : Looks for given XML element tags within the text, and calls
3005 #               the callback on pieces of text inside & outside those elements.
3006 #               Used for special handling of text inside things like CDATA
3007 #               and <programlisting>.
3008 # Arguments   : $text - the text.
3009 #               $symbol - the symbol currently being documented (only used for
3010 #                      error messages).
3011 #               $start_tag_regexp - the regular expression to match start tags.
3012 #                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3013 #                      CDATA sections or programlisting elements.
3014 #               $end_tag_func - function which is passed the matched start tag
3015 #                      and should return the appropriate end tag string regexp.
3016 #               $callback - callback called with each part of the text. It is
3017 #                      called with a piece of text, the symbol being
3018 #                      documented, and the matched start tag or "" if the text
3019 #                      is outside the XML elements being matched.
3020 #############################################################################
3021 sub ModifyXMLElements {
3022     my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3023     my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3024     my $result = "";
3026     while ($text =~ m/$start_tag_regexp/s) {
3027       $before_tag = $`; # Prematch for last successful match string
3028       $start_tag = $&;  # Last successful match
3029       $text = $';       # Postmatch for last successful match string
3031       $result .= &$callback ($before_tag, $symbol, "");
3032       $result .= $start_tag;
3034       # get the matching end-tag for current tag
3035       $end_tag_regexp = &$end_tag_func ($start_tag);
3037       if ($text =~ m/$end_tag_regexp/s) {
3038         $before_tag = $`;
3039         $end_tag = $&;
3040         $text = $';
3042         $result .= &$callback ($before_tag, $symbol, $start_tag);
3043         $result .= $end_tag;
3044       } else {
3045         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3046             "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3047         # Just assume it is all inside the tag.
3048         $result .= &$callback ($text, $symbol, $start_tag);
3049         $text = "";
3050       }
3051     }
3053     # Handle any remaining text outside the tags.
3054     $result .= &$callback ($text, $symbol, "");
3056     return $result;
3059 sub noop {
3060   return $_[0];
3063 # Adds a tag around some text.
3064 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3065 sub tagify {
3066    my ($text, $elem) = @_;
3067    return "<" . $elem . ">" . $text . "</" . $elem . ">";
3071 #############################################################################
3072 # Function    : MakeXRef
3073 # Description : This returns a cross-reference link to the given symbol.
3074 #                Though it doesn't try to do this for a few standard C types
3075 #                that it        knows won't be in the documentation.
3076 # Arguments   : $symbol - the symbol to try to create a XRef to.
3077 #               $text - text text to put inside the XRef, defaults to $symbol
3078 #############################################################################
3080 sub MakeXRef {
3081     my ($symbol, $text) = ($_[0], $_[1]);
3083     $symbol =~ s/^\s+//;
3084     $symbol =~ s/\s+$//;
3086     if (!defined($text)) {
3087         $text = $symbol;
3089         # Get rid of special suffixes ('-struct','-enum').
3090         $text =~ s/-struct$//;
3091         $text =~ s/-enum$//;
3092     }
3094     if ($symbol =~ m/ /) {
3095         return "$text";
3096     }
3098     @TRACE@("Getting type link for $symbol -> $text\n");
3100     my $symbol_id = &CreateValidSGMLID ($symbol);
3101     return "<link linkend=\"$symbol_id\">$text</link>";
3105 #############################################################################
3106 # Function    : MakeIndexterms
3107 # Description : This returns a indexterm elements for the given symbol
3108 # Arguments   : $symbol - the symbol to create indexterms for
3109 #############################################################################
3111 sub MakeIndexterms {
3112   my ($symbol, $id) = @_;
3113   my $terms =  "";
3114   my $sortas = "";
3116   # make the index useful, by ommiting the namespace when sorting
3117   if ($NAME_SPACE ne "") {
3118     if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3119        $sortas=" sortas=\"$1\"";
3120     }
3121   }
3123   if (exists $Deprecated{$symbol}) {
3124       $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3125       $IndexEntriesDeprecated{$symbol}=$id;
3126       $IndexEntriesFull{$symbol}=$id;
3127   }
3128   if (exists $Since{$symbol}) {
3129      my $since = $Since{$symbol};
3130      $since =~ s/^\s+//;
3131      $since =~ s/\s+$//;
3132      if ($since ne "") {
3133          $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3134      }
3135      $IndexEntriesSince{$symbol}=$id;
3136      $IndexEntriesFull{$symbol}=$id;
3137   }
3138   if ($terms eq "") {
3139      $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3140      $IndexEntriesFull{$symbol}=$id;
3141   }
3143   return $terms;
3146 #############################################################################
3147 # Function    : MakeDeprecationNote
3148 # Description : This returns a deprecation warning for the given symbol.
3149 # Arguments   : $symbol - the symbol to try to create a warning for.
3150 #############################################################################
3152 sub MakeDeprecationNote {
3153     my ($symbol) = $_[0];
3154     my $desc = "";
3155     if (exists $Deprecated{$symbol}) {
3156         my $note;
3158         $desc .= "<warning><para><literal>$symbol</literal> ";
3160         $note = $Deprecated{$symbol};
3162         if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3163                 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3164         } else {
3165                 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3166         }
3167         $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3168         $note =~ s/^\s+//;
3169         $note =~ s/\s+$//;
3170         if ($note ne "") {
3171             $note = &ConvertMarkDown($symbol, $note);
3172             $desc .= " " . $note;
3173         }
3174         $desc .= "</warning>\n";
3175     }
3176     return $desc;
3179 #############################################################################
3180 # Function    : MakeConditionDescription
3181 # Description : This returns a sumary of conditions for the given symbol.
3182 # Arguments   : $symbol - the symbol to try to create the sumary.
3183 #############################################################################
3185 sub MakeConditionDescription {
3186     my ($symbol) = $_[0];
3187     my $desc = "";
3189     if (exists $Deprecated{$symbol}) {
3190         if ($desc ne "") {
3191             $desc .= "|";
3192         }
3194         if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3195                 $desc .= "deprecated:$1";
3196         } else {
3197                 $desc .= "deprecated";
3198         }
3199     }
3201     if (exists $Since{$symbol}) {
3202         if ($desc ne "") {
3203             $desc .= "|";
3204         }
3206         if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3207                 $desc .= "since:$1";
3208         } else {
3209                 $desc .= "since";
3210         }
3211     }
3213     if (exists $StabilityLevel{$symbol}) {
3214         if ($desc ne "") {
3215             $desc .= "|";
3216         }
3217         $desc .= "stability:".$StabilityLevel{$symbol};
3218     }
3220     if ($desc ne "") {
3221         my $cond = $desc;
3222         $cond =~ s/\"/&quot;/g;
3223         $desc=" condition=\"".$cond."\"";
3224         @TRACE@("condition for '$symbol' = '$desc'\n");
3225     }
3226     return $desc;
3229 #############################################################################
3230 # Function    : GetHierarchy
3231 # Description : Returns the DocBook output describing the ancestors and
3232 #               immediate children of a GObject subclass. It uses the
3233 #               global @Objects and @ObjectLevels arrays to walk the tree.
3235 # Arguments   : $object - the GtkObject subclass.
3236 #               @hierarchy - previous hierarchy
3237 #############################################################################
3239 sub GetHierarchy {
3240     my ($object,$hierarchy_ref) = @_;
3241     my @hierarchy = @{$hierarchy_ref};
3242     
3243     # Find object in the objects array.
3244     my $found = 0;
3245     my @children = ();
3246     my $i;
3247     my $level;
3248     my $j;
3249     for ($i = 0; $i < @Objects; $i++) {
3250         if ($found) {
3251             if ($ObjectLevels[$i] <= $level) {
3252             last;
3253         }
3254             elsif ($ObjectLevels[$i] == $level + 1) {
3255                 push (@children, $Objects[$i]);
3256             }
3257         }
3258         elsif ($Objects[$i] eq $object) {
3259             $found = 1;
3260             $j = $i;
3261             $level = $ObjectLevels[$i];
3262         }
3263     }
3264     if (!$found) {
3265         return @hierarchy;
3266     }
3268     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3269     my @ancestors = ();
3270     push (@ancestors, $object);
3271     @TRACE@("Level: $level\n");
3272     while ($level > 1) {
3273         $j--;
3274         if ($ObjectLevels[$j] < $level) {
3275             push (@ancestors, $Objects[$j]);
3276             $level = $ObjectLevels[$j];
3277             @TRACE@("Level: $level\n");
3278         }
3279     }
3281     # Output the ancestors, indented and with links.
3282     my $last_index = 0;
3283     $level = 1;
3284     for ($i = $#ancestors; $i >= 0; $i--) {
3285         my $link_text;
3286         # Don't add a link to the current object, i.e. when i == 0.
3287         if ($i > 0) {
3288             my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]);
3289             $link_text = "<link linkend=\"$ancestor_id\">$ancestors[$i]</link>";
3290         } else {
3291             $link_text = "$ancestors[$i]";
3292         }
3293         my $indented_text = ' ' x ($level * 4) . $link_text;
3294         # Check if we already have this object
3295         my $index = -1;
3296         for ($j = 0; $j <= $#hierarchy; $j++) {
3297             if ($hierarchy[$j] eq $indented_text) {
3298                 $index = $j;
3299                 last;
3300             }
3301         }
3302         if ($index == -1) {
3303             # We have a new entry, find insert position in alphabetical order
3304             my $indent = ' ' x ($level * 4);
3305             my $found = 0;
3306             for ($j = $last_index; $j <= $#hierarchy; $j++) {
3307                 if ($hierarchy[$j] !~ m/^${indent}/) {
3308                     $last_index = $j;
3309                     $found = 1;
3310                     last;
3311                 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3312                     my $stripped_text = $hierarchy[$j];
3313                     if ($indented_text !~ m/<link linkend/) {
3314                         $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3315                         $stripped_text =~ s%</link>%%;
3316                     }
3317                     if ($indented_text lt $stripped_text) {
3318                         $last_index = $j;
3319                         $found = 1;
3320                         last;
3321                     } 
3322                 }
3323             }
3324             if (!$found) {
3325               $last_index = 1 + $#hierarchy;
3326             }
3327             splice @hierarchy, $last_index, 0, ($indented_text);
3328             $last_index++;
3329         } else {
3330             # Already have this one, remmeber index as base insert point
3331             $last_index = $index + 1;
3332         }
3333         $level++;
3334     }
3335     # Output the children, indented and with links.
3336     for ($i = 0; $i <= $#children; $i++) {
3337         my $id = &CreateValidSGMLID ($children[$i]);
3338         my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3339         splice @hierarchy, $last_index, 0, ($indented_text);
3340         $last_index++;
3341     }    
3343     return @hierarchy; 
3346 #############################################################################
3347 # Function    : GetInterfaces
3348 # Description : Returns the DocBook output describing the interfaces
3349 #               implemented by a class. It uses the global %Interfaces hash.
3350 # Arguments   : $object - the GtkObject subclass.
3351 #############################################################################
3353 sub GetInterfaces {
3354     my ($object) = @_;
3355     my $text = "";
3356     my $i;
3358     # Find object in the objects array.
3359     if (exists($Interfaces{$object})) {
3360         my @ifaces = split(' ', $Interfaces{$object});
3361         $text = <<EOF;
3362 <para>
3363 $object implements
3365         for ($i = 0; $i <= $#ifaces; $i++) {
3366             my $id = &CreateValidSGMLID ($ifaces[$i]);
3367             $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3368             if ($i < $#ifaces - 1) {
3369                 $text .= ', ';
3370             }
3371             elsif ($i < $#ifaces) {
3372                 $text .= ' and ';
3373             }
3374             else {
3375                 $text .= '.';
3376             }
3377         }
3378         $text .= <<EOF;
3379 </para>
3381     }
3383     return $text;
3386 #############################################################################
3387 # Function    : GetImplementations
3388 # Description : Returns the DocBook output describing the implementations
3389 #               of an interface. It uses the global %Interfaces hash.
3390 # Arguments   : $object - the GtkObject subclass.
3391 #############################################################################
3393 sub GetImplementations {
3394     my ($object) = @_;
3395     my @impls = ();
3396     my $text = "";
3397     my $i;
3398     foreach my $key (keys %Interfaces) {
3399         if ($Interfaces{$key} =~ /\b$object\b/) {
3400             push (@impls, $key);
3401         }
3402     }
3403     if ($#impls >= 0) {
3404         @impls = sort @impls;
3405         $text = <<EOF;
3406 <para>
3407 $object is implemented by
3409         for ($i = 0; $i <= $#impls; $i++) {
3410             my $id = &CreateValidSGMLID ($impls[$i]);
3411             $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3412             if ($i < $#impls - 1) {
3413                 $text .= ', ';
3414             }
3415             elsif ($i < $#impls) {
3416                 $text .= ' and ';
3417             }
3418             else {
3419                 $text .= '.';
3420             }
3421         }
3422         $text .= <<EOF;
3423 </para>
3425     }
3426     return $text;
3430 #############################################################################
3431 # Function    : GetPrerequisites
3432 # Description : Returns the DocBook output describing the prerequisites
3433 #               of an interface. It uses the global %Prerequisites hash.
3434 # Arguments   : $iface - the interface.
3435 #############################################################################
3437 sub GetPrerequisites {
3438     my ($iface) = @_;
3439     my $text = "";
3440     my $i;
3442     if (exists($Prerequisites{$iface})) {
3443         $text = <<EOF;
3444 <para>
3445 $iface requires
3447         my @prereqs = split(' ', $Prerequisites{$iface});
3448         for ($i = 0; $i <= $#prereqs; $i++) {
3449             my $id = &CreateValidSGMLID ($prereqs[$i]);
3450             $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3451             if ($i < $#prereqs - 1) {
3452                 $text .= ', ';
3453             }
3454             elsif ($i < $#prereqs) {
3455                 $text .= ' and ';
3456             }
3457             else {
3458                 $text .= '.';
3459             }
3460         }
3461         $text .= <<EOF;
3462 </para>
3464     }
3465     return $text;
3468 #############################################################################
3469 # Function    : GetDerived
3470 # Description : Returns the DocBook output describing the derived interfaces
3471 #               of an interface. It uses the global %Prerequisites hash.
3472 # Arguments   : $iface - the interface.
3473 #############################################################################
3475 sub GetDerived {
3476     my ($iface) = @_;
3477     my $text = "";
3478     my $i;
3480     my @derived = ();
3481     foreach my $key (keys %Prerequisites) {
3482         if ($Prerequisites{$key} =~ /\b$iface\b/) {
3483             push (@derived, $key);
3484         }
3485     }
3486     if ($#derived >= 0) {
3487         @derived = sort @derived;
3488         $text = <<EOF;
3489 <para>
3490 $iface is required by
3492         for ($i = 0; $i <= $#derived; $i++) {
3493             my $id = &CreateValidSGMLID ($derived[$i]);
3494             $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3495             if ($i < $#derived - 1) {
3496                 $text .= ', ';
3497             }
3498             elsif ($i < $#derived) {
3499                 $text .= ' and ';
3500             }
3501             else {
3502                 $text .= '.';
3503             }
3504         }
3505         $text .= <<EOF;
3506 </para>
3508     }
3509     return $text;
3513 #############################################################################
3514 # Function    : GetSignals
3515 # Description : Returns the synopsis and detailed description DocBook output
3516 #                for the signal handlers of a given GtkObject subclass.
3517 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3518 #############################################################################
3520 sub GetSignals {
3521     my ($object) = @_;
3522     my $synop = "";
3523     my $desc = "";
3525     my $i;
3526     for ($i = 0; $i <= $#SignalObjects; $i++) {
3527         if ($SignalObjects[$i] eq $object) {
3528             @TRACE@("Found signal: $SignalNames[$i]\n");
3529             my $name = $SignalNames[$i];
3530             my $symbol = "${object}::${name}";
3531             my $id = &CreateValidSGMLID ("$object-$name");
3533             $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3534             $desc .= MakeIndexterms($symbol, $id);
3535             $desc .= "\n";
3536             $desc .= OutputSymbolExtraLinks($symbol);
3538             $desc .= "<programlisting language=\"C\">";
3540             $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3541             my $type_modifier = defined($1) ? $1 : "";
3542             my $type = $2;
3543             my $pointer = $3;
3544             my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3546             my $ret_type_output = "$type_modifier$xref$pointer";
3547             my $callback_name = "user_function";
3548             $desc  .= "${ret_type_output}\n${callback_name} (";
3550             my $indentation = ' ' x (length($callback_name) + 2);
3551             my $pad = $indentation;
3553             my $sourceparams = $SourceSymbolParams{$symbol};
3554             my @params = split ("\n", $SignalPrototypes[$i]);
3555             my $j;
3556             my $l;
3557             my $type_len = length("gpointer");
3558             my $name_len = length("user_data");
3559             # do two passes, the first one is to calculate padding
3560             for ($l = 0; $l < 2; $l++) {
3561                 for ($j = 0; $j <= $#params; $j++) {
3562                     my $param_name;
3563                     # allow alphanumerics, '_', '[' & ']' in param names
3564                     if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3565                         $type = $1;
3566                         $pointer = $2;
3567                         if (defined($sourceparams)) {
3568                             $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3569                         }
3570                         else {
3571                             $param_name = $3;
3572                         }
3573                         if (!defined($param_name)) {
3574                             $param_name = "arg$j";
3575                         }
3576                         if ($l == 0) {
3577                             if (length($type) + length($pointer) > $type_len) {
3578                                 $type_len = length($type) + length($pointer);
3579                             }
3580                             if (length($param_name) > $name_len) {
3581                                 $name_len = length($param_name);
3582                             }
3583                         }
3584                         else {
3585                             $xref = &MakeXRef ($type, &tagify($type, "type"));
3586                             $pad = ' ' x ($type_len - length($type) - length($pointer));
3587                             $desc .= "$xref$pad $pointer${param_name},\n";
3588                             $desc .= $indentation;
3589                         }
3590                     } else {
3591                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3592                              "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3593                     }
3594                 }
3595             }
3596             $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3597             $pad = ' ' x ($type_len - length("gpointer"));
3598             $desc  .= "$xref$pad user_data)";
3599             $desc  .= "</programlisting>\n";
3601             my $flags = $SignalFlags[$i];
3602             my $flags_string = "";
3604             if (defined ($flags)) {
3605               if ($flags =~ m/f/) {
3606                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3607               }
3608               elsif ($flags =~ m/l/) {
3609                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3610               }
3611               elsif ($flags =~ m/c/) {
3612                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3613                 $flags_string = "Cleanup";
3614               }
3615               if ($flags =~ m/r/) {
3616                 if ($flags_string) { $flags_string .= " / "; }
3617                 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3618               }
3619               if ($flags =~ m/d/) {
3620                 if ($flags_string) { $flags_string .= " / "; }
3621                 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3622               }
3623               if ($flags =~ m/a/) {
3624                 if ($flags_string) { $flags_string .= " / "; }
3625                 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3626               }
3627               if ($flags =~ m/h/) {
3628                 if ($flags_string) { $flags_string .= " / "; }
3629                 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3630               }
3631             }
3633             $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";
3635             my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3637             $AllSymbols{$symbol} = 1;
3638             if (defined ($SymbolDocs{$symbol})) {
3639                 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3641                 $desc .= $symbol_docs;
3643                 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3644                     $AllDocumentedSymbols{$symbol} = 1;
3645                 }
3646             }
3647             $desc .= &MakeDeprecationNote($symbol);
3649             $desc .= $parameters;
3650             if ($flags_string) {
3651                 $desc  .= "<para>Flags: $flags_string</para>\n";
3652             }
3653             $desc .= OutputSymbolTraits ($symbol);
3654             $desc .= "</refsect2>";
3655         }
3656     }
3657     return ($synop, $desc);
3661 #############################################################################
3662 # Function    : GetArgs
3663 # Description : Returns the synopsis and detailed description DocBook output
3664 #                for the Args of a given GtkObject subclass.
3665 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3666 #############################################################################
3668 sub GetArgs {
3669     my ($object) = @_;
3670     my $synop = "";
3671     my $desc = "";
3672     my $child_synop = "";
3673     my $child_desc = "";
3674     my $style_synop = "";
3675     my $style_desc = "";
3677     my $i;
3678     for ($i = 0; $i <= $#ArgObjects; $i++) {
3679         if ($ArgObjects[$i] eq $object) {
3680             @TRACE@("Found arg: $ArgNames[$i]\n");
3681             my $name = $ArgNames[$i];
3682             my $flags = $ArgFlags[$i];
3683             my $flags_string = "";
3684             my $kind = "";
3685             my $id_sep = "";
3687             if ($flags =~ m/c/) {
3688                 $kind = "child property";
3689                 $id_sep = "c-";
3690             }
3691             elsif ($flags =~ m/s/) {
3692                 $kind = "style property";
3693                 $id_sep = "s-";
3694             }
3695             else {
3696                 $kind = "property";
3697             }
3699             # Remember only one colon so we don't clash with signals.
3700             my $symbol = "${object}:${name}";
3701             # use two dashes and ev. an extra separator here for the same reason.
3702             my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3704             my $type = $ArgTypes[$i];
3705             my $type_output;
3706             my $range = $ArgRanges[$i];
3707             my $range_output = CreateValidSGML ($range);
3708             my $default = $ArgDefaults[$i];
3709             my $default_output = CreateValidSGML ($default);
3711             if ($type eq "GtkString") {
3712                 $type = "char&#160;*";
3713             }
3714             if ($type eq "GtkSignal") {
3715                 $type = "GtkSignalFunc, gpointer";
3716                 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3717                     . &MakeXRef ("gpointer");
3718             } elsif ($type =~ m/^(\w+)\*$/) {
3719                 $type_output = &MakeXRef ($1, &tagify($1, "type")) . "&#160;*";
3720             } else {
3721                 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3722             }
3724             if ($flags =~ m/r/) {
3725                 $flags_string = "Read";
3726             }
3727             if ($flags =~ m/w/) {
3728                 if ($flags_string) { $flags_string .= " / "; }
3729                 $flags_string .= "Write";
3730             }
3731             if ($flags =~ m/x/) {
3732                 if ($flags_string) { $flags_string .= " / "; }
3733                 $flags_string .= "Construct";
3734             }
3735             if ($flags =~ m/X/) {
3736                 if ($flags_string) { $flags_string .= " / "; }
3737                 $flags_string .= "Construct Only";
3738             }
3740             $AllSymbols{$symbol} = 1;
3741             my $blurb;
3742             if (defined($SymbolDocs{$symbol}) &&
3743                 !IsEmptyDoc($SymbolDocs{$symbol})) {
3744                 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3745                 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3746                 $AllDocumentedSymbols{$symbol} = 1;
3747             }
3748             else {
3749                 if (!($ArgBlurbs[$i] eq "")) {
3750                     $AllDocumentedSymbols{$symbol} = 1;
3751                 } else {
3752                     # FIXME: print a warning?
3753                     @TRACE@(".. no description\n");
3754                 }
3755                 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3756             }
3758             my $pad1 = " " x (24 - length ($name));
3760             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";
3761             my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3762             $arg_desc .= MakeIndexterms($symbol, $id);
3763             $arg_desc .= "\n";
3764             $arg_desc .= OutputSymbolExtraLinks($symbol);
3766             $arg_desc .= "<programlisting>  “$name”$pad1 $type_output</programlisting>\n";
3767             $arg_desc .= $blurb;
3768             $arg_desc .= &MakeDeprecationNote($symbol);
3770             if ($flags_string) {
3771               $arg_desc  .= "<para>Flags: $flags_string</para>\n";
3772             }
3773             if ($range ne "") {
3774                 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3775             }
3776             if ($default ne "") {
3777                 $arg_desc .= "<para>Default value: $default_output</para>\n";
3778             }
3779             $arg_desc .= OutputSymbolTraits ($symbol);
3780             $arg_desc .= "</refsect2>\n";
3782             if ($flags =~ m/c/) {
3783                 $child_synop .= $arg_synop;
3784                 $child_desc .= $arg_desc;
3785             }
3786             elsif ($flags =~ m/s/) {
3787                 $style_synop .= $arg_synop;
3788                 $style_desc .= $arg_desc;
3789             }
3790             else {
3791                 $synop .= $arg_synop;
3792                 $desc .= $arg_desc;
3793             }
3794         }
3795     }
3796     return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3800 #############################################################################
3801 # Function    : ReadSourceDocumentation
3802 # Description : This reads in the documentation embedded in comment blocks
3803 #                in the source code (for Gnome).
3805 #                Parameter descriptions override any in the template files.
3806 #                Function descriptions are placed before any description from
3807 #                the template files.
3809 #                It recursively descends the source directory looking for .c
3810 #                files and scans them looking for specially-formatted comment
3811 #                blocks.
3813 # Arguments   : $source_dir - the directory to scan.
3814 #############m###############################################################
3816 sub ReadSourceDocumentation {
3817     my ($source_dir) = @_;
3818     my ($file, $dir, @suffix_list, $suffix);
3820     # prepend entries from @SOURCE_DIR
3821     for my $dir (@SOURCE_DIRS) {
3822         # Check if the filename is in the ignore list.
3823         if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3824             @TRACE@("Skipping source directory: $source_dir");
3825             return;
3826         }
3827     }
3829     @TRACE@("Scanning source directory: $source_dir");
3831     # This array holds any subdirectories found.
3832     my (@subdirs) = ();
3834     @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3836     opendir (SRCDIR, $source_dir)
3837         || die "Can't open source directory $source_dir: $!";
3839     foreach $file (readdir (SRCDIR)) {
3840       if ($file =~ /^\./) {
3841         next;
3842       } elsif (-d "$source_dir/$file") {
3843         push (@subdirs, $file);
3844       } elsif (@suffix_list) {
3845         foreach $suffix (@suffix_list) {
3846           if ($file =~ m/\.\Q${suffix}\E$/) {
3847             &ScanSourceFile ("$source_dir/$file");
3848           }
3849         }
3850       } elsif ($file =~ m/\.[ch]$/) {
3851         &ScanSourceFile ("$source_dir/$file");
3852       }
3853     }
3854     closedir (SRCDIR);
3856     # Now recursively scan the subdirectories.
3857     foreach $dir (@subdirs) {
3858         &ReadSourceDocumentation ("$source_dir/$dir");
3859     }
3863 #############################################################################
3864 # Function    : ScanSourceFile
3865 # Description : Scans one source file looking for specially-formatted comment
3866 #                blocks. Later &MergeSourceDocumentation is used to merge any
3867 #                documentation found with the documentation already read in
3868 #                from the template files.
3870 # Arguments   : $file - the file to scan.
3871 #############################################################################
3873 sub ScanSourceFile {
3874     my ($file) = @_;
3875     my $basename;
3877     # prepend entries from @SOURCE_DIR
3878     for my $dir (@SOURCE_DIRS) {
3879         # Check if the filename is in the ignore list.
3880         if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3881             @TRACE@("Skipping source file: $file");
3882             return;
3883         }
3884     }
3886     if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3887         $basename = $1;
3888     } else {
3889         &LogWarning ($file, 1, "Can't find basename for this filename.");
3890         $basename = $file;
3891     }
3893     # Check if the basename is in the list of files to ignore.
3894     if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3895         @TRACE@("Skipping source file: $file");
3896         return;
3897     }
3899     @TRACE@("Scanning source file: $file");
3901     open (SRCFILE, $file)
3902         || die "Can't open $file: $!";
3903     my $in_comment_block = 0;
3904     my $symbol;
3905     my $in_part = "";
3906     my ($description, $return_desc);
3907     my ($since_desc, $stability_desc, $deprecated_desc);
3908     my $current_param;
3909     my @params;
3910     while (<SRCFILE>) {
3911         # Look for the start of a comment block.
3912         if (!$in_comment_block) {
3913             if (m%^\s*/\*.*\*/%) {
3914                 #one-line comment - not gtkdoc
3915             } elsif (m%^\s*/\*\*\s%) {
3916                 @TRACE@("Found comment block start\n");
3918                 $in_comment_block = 1;
3920                 # Reset all the symbol data.
3921                 $symbol = "";
3922                 $in_part = "";
3923                 $description = "";
3924                 $return_desc = "";
3925                 $since_desc = "";
3926                 $deprecated_desc = "";
3927                 $stability_desc = "";
3928                 $current_param = -1;
3929                 @params = ();
3930             }
3931             next;
3932         }
3934         # We're in a comment block. Check if we've found the end of it.
3935         if (m%^\s*\*+/%) {
3936             if (!$symbol) {
3937                 # maybe its not even meant to be a gtk-doc comment?
3938                 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3939             } else {
3940                 # Add the return value description onto the end of the params.
3941                 if ($return_desc) {
3942                     # TODO(ensonic): check for duplicated Return docs
3943                     # &LogWarning ($file, $., "Multiple Returns for $symbol.");
3944                     push (@params, "Returns");
3945                     push (@params, $return_desc);
3946                 }
3947                 # Convert special SGML characters
3948                 $description = &ConvertSGMLChars ($symbol, $description);
3949                 my $k;
3950                 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3951                     $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3952                 }
3954                 # Handle Section docs
3955                 if ($symbol =~ m/SECTION:\s*(.*)/) {
3956                     my $real_symbol=$1;
3957                     my $key;
3959                     if (scalar %KnownSymbols) {
3960                         if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
3961                             &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
3962                         }
3963                     }
3965                     @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
3966                     for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3967                         @TRACE@("   '".$params[$k]."'\n");
3968                         $params[$k] = "\L$params[$k]";
3969                         undef $key;
3970                         if ($params[$k] eq "short_description") {
3971                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
3972                         } elsif ($params[$k] eq "see_also") {
3973                             $key = "$TMPL_DIR/$real_symbol:See_Also";
3974                         } elsif ($params[$k] eq "title") {
3975                             $key = "$TMPL_DIR/$real_symbol:Title";
3976                         } elsif ($params[$k] eq "stability") {
3977                             $key = "$TMPL_DIR/$real_symbol:Stability_Level";
3978                         } elsif ($params[$k] eq "section_id") {
3979                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
3980                         } elsif ($params[$k] eq "include") {
3981                             $key = "$TMPL_DIR/$real_symbol:Include";
3982                         } elsif ($params[$k] eq "image") {
3983                             $key = "$TMPL_DIR/$real_symbol:Image";
3984                         }
3985                         if (defined($key)) {
3986                             $SourceSymbolDocs{$key}=$params[$k+1];
3987                             $SourceSymbolSourceFile{$key} = $file;
3988                             $SourceSymbolSourceLine{$key} = $.;
3989                         }
3990                     }
3991                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
3992                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
3993                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
3994                     #$SourceSymbolTypes{$symbol} = "SECTION";
3995                 } else {
3996                     @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
3997                     $SourceSymbolDocs{$symbol} = $description;
3998                     $SourceSymbolParams{$symbol} = [ @params ];
3999                     # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4000                     #if (defined $DeclarationTypes{$symbol}) {
4001                     #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4002                     #}
4003                     $SourceSymbolSourceFile{$symbol} = $file;
4004                     $SourceSymbolSourceLine{$symbol} = $.;
4005                 }
4007                 if ($since_desc) {
4008                      ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4009                      $since_desc =~ s/^\s+//;
4010                      $since_desc =~ s/\s+$//;
4011                      @TRACE@("Since($symbol) : [$since_desc]\n");
4012                      $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4013                      if(scalar @extra_lines) {
4014                          &LogWarning ($file, $., "multi-line since docs found");
4015                      }
4016                 }
4018                 if ($stability_desc) {
4019                     $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4020                     $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4021                 }
4023                 if ($deprecated_desc) {
4024                     if (!exists $Deprecated{$symbol}) {
4025                          # don't warn for signals and properties
4026                          #if ($symbol !~ m/::?(.*)/) {
4027                          if (defined $DeclarationTypes{$symbol}) {
4028                              &LogWarning ($file, $.,
4029                                  "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4030                                  " (See the --deprecated-guards option for gtkdoc-scan.)");
4031                          }
4032                     }
4033                     $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4034                 }
4035             }
4037             $in_comment_block = 0;
4038             next;
4039         }
4041         # Get rid of ' * ' at start of every line in the comment block.
4042         s%^\s*\*\s?%%;
4043         # But make sure we don't get rid of the newline at the end.
4044         if (!$_) {
4045             $_ = "\n";
4046         }
4047         @TRACE@("scanning :$_");
4049         # If we haven't found the symbol name yet, look for it.
4050         if (!$symbol) {
4051             if (m%^\s*(SECTION:\s*\S+)%) {
4052                 $symbol = $1;
4053                 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4054             } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-a-z0-9_ ]+\)\s*)*$%) {
4055                 $symbol = $1;
4056                 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4057             }
4058             next;
4059         }
4061         if ($in_part eq "description") {
4062             # Get rid of 'Description:'
4063             s%^\s*Description:%%;
4064         }
4066         if (m%^\s*(returns|return\s+value):%i) {
4067             # we're in param section and have not seen the blank line
4068             if($in_part ne "") {
4069               $return_desc = $';
4070               $in_part = "return";
4071               next;
4072             }
4073         } elsif (m%^\s*since:%i) {
4074             # we're in param section and have not seen the blank line
4075             if($in_part ne "") {
4076               $since_desc = $';
4077               $in_part = "since";
4078               next;
4079             }
4080         } elsif (m%^\s*deprecated:%i) {
4081             # we're in param section and have not seen the blank line
4082             if($in_part ne "") {
4083               $deprecated_desc = $';
4084               $in_part = "deprecated";
4085               next;
4086             }
4087         } elsif (m%^\s*stability:%i) {
4088             $stability_desc = $';
4089             $in_part = "stability";
4090             next;
4091         }
4093         if ($in_part eq "description") {
4094             $description .= $_;
4095             next;
4096         } elsif ($in_part eq "return") {
4097             $return_desc .= $_;
4098             next;
4099         } elsif ($in_part eq "since") {
4100             $since_desc .= $_;
4101             next;
4102         } elsif ($in_part eq "stability") {
4103             $stability_desc .= $_;
4104             next;
4105         } elsif ($in_part eq "deprecated") {
4106             $deprecated_desc .= $_;
4107             next;
4108         }
4110         # We must be in the parameters. Check for the empty line below them.
4111         if (m%^\s*$%) {
4112             $in_part = "description";
4113             next;
4114         }
4116         # Look for a parameter name.
4117         if (m%^\s*@(\S+)\s*:\s*%) {
4118             my $param_name = $1;
4119             my $param_desc = $';
4121             @TRACE@("Found parameter: $param_name\n");
4122             # Allow varargs variations
4123             if ($param_name =~ m/^\.\.\.$/) {
4124                 $param_name = "...";
4125             }
4126             @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4128             push (@params, $param_name);
4129             push (@params, $param_desc);
4130             $current_param += $PARAM_FIELD_COUNT;
4131             next;
4132         }
4134         # We must be in the middle of a parameter description, so add it on
4135         # to the last element in @params.
4136         if ($current_param == -1) {
4137             &LogWarning ($file, $., "Parsing comment block file : parameter expected.");
4138         } else {
4139             $params[$#params] .= $_;
4140         }
4141     }
4142     close (SRCFILE);
4145 #############################################################################
4146 # Function    : OutputMissingDocumentation
4147 # Description : Outputs report of documentation coverage to a file
4149 # Arguments   : none
4150 #############################################################################
4152 sub OutputMissingDocumentation {
4153     my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4154     my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4156     my $n_documented = 0;
4157     my $n_incomplete = 0;
4158     my $total = 0;
4159     my $symbol;
4160     my $percent;
4161     my $msg;
4162     my $buffer = "";
4163     my $buffer_deprecated = "";
4164     my $buffer_descriptions = "";
4166     open(UNDOCUMENTED, ">$new_undocumented_file")
4167       || die "Can't create $new_undocumented_file";
4169     foreach $symbol (sort (keys (%AllSymbols))) {
4170         # FIXME: should we print LogWarnings for undocumented stuff?
4171         # DEBUG
4172         #my $ssfile = &GetSymbolSourceFile($symbol);
4173         #my $ssline = &GetSymbolSourceLine($symbol);
4174         #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4175         # DEBUG
4176         if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4177             $total++;
4178             if (exists ($AllDocumentedSymbols{$symbol})) {
4179                 $n_documented++;
4180                 if (exists ($AllIncompleteSymbols{$symbol})) {
4181                     $n_incomplete++;
4182                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4183                     #$buffer .= "\t0: ".$location;
4184                 }
4185             } elsif (exists $Deprecated{$symbol}) {
4186                 if (exists ($AllIncompleteSymbols{$symbol})) {
4187                     $n_incomplete++;
4188                     $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4189                     #$buffer .= "\t1a: ".$location;
4190                 } else {
4191                     $buffer_deprecated .= $symbol . "\n";
4192                     #$buffer .= "\t1b: ".$location;
4193                 }
4194             } else {
4195                 if (exists ($AllIncompleteSymbols{$symbol})) {
4196                     $n_incomplete++;
4197                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4198                     #$buffer .= "\t2a: ".$location;
4199                 } else {
4200                     $buffer .= $symbol . "\n";
4201                     #$buffer .= "\t2b: ".$location;
4202                 }
4203             }
4204         } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4205             $total++;
4206             if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4207             || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4208               $n_documented++;
4209             } else {
4210               # cut off the leading namespace ($TMPL_DIR)
4211               $symbol =~ m/^.*\/(.*)$/;
4212               $buffer_descriptions .= $1 . "\n";
4213             }
4214         }
4215     }
4217     $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
4219     if ($total == 0) {
4220       $percent = 100;
4221     } else {
4222       $percent = ($n_documented / $total) * 100.0;
4223     }
4225     printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4226     print UNDOCUMENTED "$n_documented symbols documented.\n";
4227     print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4228     print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
4230     print UNDOCUMENTED $buffer;
4231     close (UNDOCUMENTED);
4233     return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4235     printf "%.0f%% symbol docs coverage", $percent;
4236     print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4237     print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4241 #############################################################################
4242 # Function    : OutputUndeclaredSymbols
4243 # Description : Outputs symbols that are listed in the section file, but not
4244 #               declaration is found in the sources
4246 # Arguments   : none
4247 #############################################################################
4249 sub OutputUndeclaredSymbols {
4250     my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4251     my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4253     open(UNDECLARED, ">$new_undeclared_file")
4254         || die "Can't create $new_undeclared_file";
4256     if (%UndeclaredSymbols) {
4257         print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4258         print UNDECLARED "\n";
4259         print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4260     }
4261     close(UNDECLARED);
4263     return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4266 #############################################################################
4267 # Function    : OutputUnusedSymbols
4268 # Description : Outputs symbols that are documented in comments, but not
4269 #               declared in the sources
4271 # Arguments   : none
4272 #############################################################################
4274 sub OutputUnusedSymbols {
4275     my $num_unused = 0;
4276     my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4277     my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4279     open (UNUSED, ">$new_unused_file")
4280         || die "Can't open $new_unused_file";
4281     my ($symbol);
4282     foreach $symbol (sort keys (%Declarations)) {
4283         if (!defined ($DeclarationOutput{$symbol})) {
4284             print (UNUSED "$symbol\n");
4285             $num_unused++;
4286         }
4287     }
4288     foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4289         print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4290         $num_unused++;
4291     }
4292     close (UNUSED);
4293     if ($num_unused != 0) {
4294         &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4295             "They should be added to $MODULE-sections.txt in the appropriate place.");
4296     }
4298     return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4302 #############################################################################
4303 # Function    : OutputAllSymbols
4304 # Description : Outputs list of all symbols to a file
4306 # Arguments   : none
4307 #############################################################################
4309 sub OutputAllSymbols {
4310      my $n_documented = 0;
4311      my $total = 0;
4312      my $symbol;
4313      my $percent;
4314      my $msg;
4316      open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4317           || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4319      foreach $symbol (sort (keys (%AllSymbols))) {
4320           print SYMBOLS $symbol . "\n";
4321      }
4323      close (SYMBOLS);
4326 #############################################################################
4327 # Function    : OutputSymbolsWithoutSince
4328 # Description : Outputs list of all symbols without a since tag to a file
4330 # Arguments   : none
4331 #############################################################################
4333 sub OutputSymbolsWithoutSince {
4334      my $n_documented = 0;
4335      my $total = 0;
4336      my $symbol;
4337      my $percent;
4338      my $msg;
4340      open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4341           || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4343      foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4344          if (!defined $Since{$symbol}) {
4345              print SYMBOLS $symbol . "\n";
4346          }
4347      }
4349      close (SYMBOLS);
4353 #############################################################################
4354 # Function    : MergeSourceDocumentation
4355 # Description : This merges documentation read from a source file into the
4356 #                documentation read in from a template file.
4358 #                Parameter descriptions override any in the template files.
4359 #                Function descriptions are placed before any description from
4360 #                the template files.
4362 # Arguments   : none
4363 #############################################################################
4365 sub MergeSourceDocumentation {
4366     my $symbol;
4367     my @Symbols;
4369     if (scalar %SymbolDocs) {
4370         @Symbols=keys (%SymbolDocs);
4371         @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4372     }
4373     else {
4374         # filter scanned declarations, with what we suppress from -sections.txt
4375         my %tmp = ();
4376         foreach $symbol (keys (%Declarations)) {
4377             if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4378                 $tmp{$symbol}=1;
4379             }
4380         }
4381         # , add the rest from -sections.txt
4382         foreach $symbol (keys (%KnownSymbols)) {
4383             if ($KnownSymbols{$symbol} == 1) {
4384                 $tmp{$symbol}=1;
4385             }
4386         }
4387         # and add whats found in the source
4388         foreach $symbol (keys (%SourceSymbolDocs)) {
4389             $tmp{$symbol}=1;
4390         }
4391         @Symbols = keys (%tmp);
4392         @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4393     }
4394     foreach $symbol (@Symbols) {
4395         $AllSymbols{$symbol} = 1;
4397         my $have_tmpl_docs = 0;
4399         ## see if the symbol is documented in template
4400         my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4401         my $check_tmpl_doc =$tmpl_doc;
4402         # remove all xml-tags and whitespaces
4403         $check_tmpl_doc =~ s/<.*?>//g;
4404         $check_tmpl_doc =~ s/\s//g;
4405         # anything left ?
4406         if ($check_tmpl_doc ne "") {
4407             $have_tmpl_docs = 1;
4408         } else {
4409             # if the docs have just an empty para, don't merge that.
4410             $check_tmpl_doc = $tmpl_doc;
4411             $check_tmpl_doc =~ s/(\s|\n)//msg;
4412             if ($check_tmpl_doc eq "<para></para>") {
4413                $tmpl_doc = "";
4414             }
4415         }
4417         if (exists ($SourceSymbolDocs{$symbol})) {
4418             my $type = $DeclarationTypes {$symbol};
4420             @TRACE@("merging [$symbol] from source\n");
4422             my $item = "Parameter";
4423             if (defined ($type)) {
4424                 if ($type eq 'STRUCT') {
4425                     $item = "Field";
4426                 } elsif ($type eq 'ENUM') {
4427                     $item = "Value";
4428                 } elsif ($type eq 'UNION') {
4429                     $item = "Field";
4430                 }
4431             } else {
4432                 $type="SIGNAL";
4433             }
4435             my $src_doc = $SourceSymbolDocs{$symbol};
4436             # remove leading and training whitespaces
4437             $src_doc =~ s/^\s+//;
4438             $src_doc =~ s/\s+$//;
4440             # Don't output warnings for overridden titles as titles are
4441             # automatically generated in the -sections.txt file, and thus they
4442             # are often overridden.
4443             if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4444                 # check if content is different
4445                 if ($tmpl_doc ne $src_doc) {
4446                     #print "[$tmpl_doc] [$src_doc]\n";
4447                     &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4448                         "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4449                 }
4450             }
4452             if ($src_doc ne "") {
4453                  $AllDocumentedSymbols{$symbol} = 1;
4454             }
4456             # Do not add <para> to nothing, it breaks missing docs checks.
4457             my $src_doc_para = "";
4458             if ($src_doc ne "") {
4459                 $src_doc_para = $src_doc;
4460             }
4462             if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4463                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4464             } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4465                 # For the title/summary/see also section docs we don't want to
4466                 # add any <para> tags.
4467                 $SymbolDocs{$symbol} = "$src_doc"
4468             } else {
4469                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4470             }
4472             # merge parameters
4473             if ($symbol =~ m/.*::.*/) {
4474                 # For signals we prefer the param names from the source docs,
4475                 # since the ones from the templates are likely to contain the
4476                 # artificial argn names which are generated by gtkdoc-scangobj.
4477                 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4478                 # FIXME: we need to check for empty docs here as well!
4479             } else {
4480                 # The templates contain the definitive parameter names and order,
4481                 # so we will not change that. We only override the actual text.
4482                 my $tmpl_params = $SymbolParams{$symbol};
4483                 if (!defined ($tmpl_params)) {
4484                     @TRACE@("No merge needed for $symbol\n");
4485                     $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4486                     #  FIXME: we still like to get the number of params and merge
4487                     #  1) we would noticed that params have been removed/renamed
4488                     #  2) we would catch undocumented params
4489                     #  params are not (yet) exported in -decl.txt so that we
4490                     #  could easily grab them :/
4491                 } else {
4492                     my $params = $SourceSymbolParams{$symbol};
4493                     my $j;
4494                     @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4495                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4496                         my $tmpl_param_name = $$tmpl_params[$j];
4498                         # Try to find the param in the source comment documentation.
4499                         my $found = 0;
4500                         my $k;
4501                         @TRACE@("  try merge param $tmpl_param_name\n");
4502                         for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4503                             my $param_name = $$params[$k];
4504                             my $param_desc = $$params[$k + 1];
4506                             @TRACE@("    test param  $param_name\n");
4507                             # We accept changes in case, since the Gnome source
4508                             # docs contain a lot of these.
4509                             if ("\L$param_name" eq "\L$tmpl_param_name") {
4510                                 $found = 1;
4512                                 # Override the description.
4513                                 $$tmpl_params[$j + 1] = $param_desc;
4515                                 # Set the name to "" to mark it as used.
4516                                 $$params[$k] = "";
4517                                 last;
4518                             }
4519                         }
4521                         # If it looks like the parameters are there, but not
4522                         # in the right place, try to explain a bit better.
4523                         if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4524                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4525                                 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4526                         }
4527                     }
4529                     # Now we output a warning if parameters have been described which
4530                     # do not exist.
4531                     for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4532                         my $param_name = $$params[$j];
4533                         if ($param_name) {
4534                             # the template builder cannot detect if a macro returns
4535                             # a result or not
4536                             if(($type eq "MACRO") && ($param_name eq "Returns")) {
4537                                 # FIXME: do we need to add it then to tmpl_params[] ?
4538                                 my $num=$#$tmpl_params;
4539                                 @TRACE@("  adding Returns: to macro docs for $symbol.\n");
4540                                 $$tmpl_params[$num+1]="Returns";
4541                                 $$tmpl_params[$num+2]=$$params[$j+1];
4542                                 next;
4543                             }
4544                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4545                                 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4546                         }
4547                     }
4548                 }
4549             }
4550         } else {
4551             if ($have_tmpl_docs) {
4552                 $AllDocumentedSymbols{$symbol} = 1;
4553                 @TRACE@("merging [$symbol] from template\n");
4554             }
4555             else {
4556                 @TRACE@("[$symbol] undocumented\n");
4557             }
4558         }
4560         # if this symbol is documented, check if docs are complete
4561         $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4562         # remove all xml-tags and whitespaces
4563         $check_tmpl_doc =~ s/<.*?>//g;
4564         $check_tmpl_doc =~ s/\s//g;
4565         if ($check_tmpl_doc ne "") {
4566             my $tmpl_params = $SymbolParams{$symbol};
4567             if (defined ($tmpl_params)) {
4568                 my $type = $DeclarationTypes {$symbol};
4570                 my $item = "Parameter";
4571                 if (defined ($type)) {
4572                     if ($type eq 'STRUCT') {
4573                         $item = "Field";
4574                     } elsif ($type eq 'ENUM') {
4575                         $item = "Value";
4576                     } elsif ($type eq 'UNION') {
4577                         $item = "Field";
4578                     }
4579                 } else {
4580                     $type="SIGNAL";
4581                 }
4583                 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4585                 if ($#$tmpl_params > 0) {
4586                     my $j;
4587                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4588                         # Output a warning if the parameter is empty and
4589                         # remember for stats.
4590                         my $tmpl_param_name = $$tmpl_params[$j];
4591                         my $tmpl_param_desc = $$tmpl_params[$j + 1];
4592                         if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4593                             if (exists ($AllIncompleteSymbols{$symbol})) {
4594                                 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4595                             } else {
4596                                 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4597                             }
4598                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4599                                 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4600                         }
4601                     }
4602                 }
4603                 else {
4604                     if ($#$tmpl_params == 0) {
4605                         $AllIncompleteSymbols{$symbol}="<items>";
4606                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4607                             "$item descriptions for $symbol are missing in source code comment block.");
4608                     }
4609                     # $#$tmpl_params==-1 means we don't know about parameters
4610                     # this unfortunately does not tell if there should be some
4611                 }
4612             }
4613         }
4614    }
4615    @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4618 #############################################################################
4619 # Function    : IsEmptyDoc
4620 # Description : Check if a doc-string is empty. Its also regarded as empty if
4621 #               it only consist of whitespace or e.g. FIXME.
4622 # Arguments   : the doc-string
4623 #############################################################################
4624 sub IsEmptyDoc {
4625     my ($doc) = @_;
4627     if ($doc =~ /^\s*$/) {
4628         return 1;
4629     }
4631     if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4632         return 1;
4633     }
4635     return 0;
4638 #############################################################################
4639 # Function    : ConvertMarkDown
4640 # Description : Converts mark down syntax to the respective docbook.
4641 #               http://de.wikipedia.org/wiki/Markdown
4642 #               Inspired by the design of ParseDown
4643 #               http://parsedown.org/
4644 #               Copyright (c) 2013 Emanuil Rusev, erusev.com
4645 # Arguments   : the symbol name, the doc-string
4646 #############################################################################
4648 sub ConvertMarkDown {
4649     my ($symbol, $text) = @_;
4651     $text = &MarkDownParse ($text, $symbol);
4653     return $text
4656 # SUPPORTED MARKDOWN
4657 # ==================
4659 # Atx-style Headers
4660 # -----------------
4662 # # Header 1
4664 # ## Header 2 ##
4666 # Setext-style Headers
4667 # --------------------
4669 # Header 1
4670 # ========
4672 # Header 2
4673 # --------
4675 # Ordered (unnested) Lists
4676 # ------------------------
4678 # 1. item 1
4680 # 1. item 2 with loooong
4681 #    description
4683 # 3. item 3
4685 # Note: we require a blank line above the list items
4688 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4690 sub MarkDownParseBlocks {
4691   my ($linesref, $symbol, $context) = @_;
4692   my $line;
4693   my @md_blocks = ();
4694   my $md_block = { type => "" };
4696  OUTER: foreach $line (@$linesref) {
4697     my $first_char = substr ($line, 0, 1);
4698     my $deindented_line;
4700     if ($md_block->{"type"} eq "markup") {
4701       if (!$md_block->{"closed"}) {
4702         if (index ($line, $md_block->{"start"}) != -1) {
4703           $md_block->{"depth"}++;
4704         }
4705         if (index ($line, $md_block->{"end"}) != -1) {
4706           if ($md_block->{"depth"} > 0) {
4707             $md_block->{"depth"}--;
4708           } else {
4709             $md_block->{"closed"} = 1;
4710           }
4711         }
4712         $md_block->{"text"} .= "\n" . $line;
4713         next OUTER;
4714       }
4715     }
4717     $deindented_line = $line;
4718     $deindented_line =~ s/^\s+//;
4720     if ($md_block->{"type"} eq "heading") {
4721       # a heading is ended by any level less than or equal
4722       if ($md_block->{"level"} == 1) {
4723         if ($line =~ /^={4,}[ \t]*$/) {
4724           my $text = pop @{$md_block->{"lines"}};
4725           $md_block->{"interrupted"} = 0;
4726           push @md_blocks, $md_block;
4728           $md_block = { type => "heading",
4729                         text => $text,
4730                         lines => [],
4731                         level => 1 };
4732           next OUTER;
4733         } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4734           $md_block->{"interrupted"} = 0;
4735           push @md_blocks, $md_block;
4737           $md_block = { type => "heading",
4738                         text => $1,
4739                         id => $2,
4740                         lines => [],
4741                         level => 1 };
4742           next OUTER;
4743         } else {
4744           # push lines into the block until the end is reached
4745           push @{$md_block->{"lines"}}, $line;
4746           next OUTER;
4747         }
4748       } else {
4749         if ($line =~ /^[=]{4,}[ \t]*$/) {
4750           my $text = pop @{$md_block->{"lines"}};
4751           $md_block->{"interrupted"} = 0;
4752           push @md_blocks, $md_block;
4754           $md_block = { type => "heading",
4755                         text => $text,
4756                         lines => [],
4757                         level => 1 };
4758           next OUTER;
4759         } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4760           my $text = pop @{$md_block->{"lines"}};
4761           $md_block->{"interrupted"} = 0;
4762           push @md_blocks, $md_block;
4764           $md_block = { type => "heading",
4765                         text => $text,
4766                         lines => [],
4767                         level => 2 };
4768           next OUTER;
4769         } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4770           $md_block->{"interrupted"} = 0;
4771           push @md_blocks, $md_block;
4773           $md_block = { type => "heading",
4774                         text => $2,
4775                         id => $3,
4776                         lines => [],
4777                         level => length($1) };
4778           next OUTER;
4779         } else {
4780           # push lines into the block until the end is reached
4781           push @{$md_block->{"lines"}}, $line;
4782           next OUTER;
4783         }
4784       }
4785     } elsif ($md_block->{"type"} eq "code") {
4786       if ($line =~ /^[ \t]*\]\|/) {
4787         push @md_blocks, $md_block;
4788         $md_block = { type => "paragraph",
4789                       text => "",
4790                       lines => [] };
4791       } else {
4792         push @{$md_block->{"lines"}}, $line;
4793       }
4794       next OUTER;
4795     }
4797     if ($deindented_line eq "") {
4798       $md_block->{"interrupted"} = 1;
4799       next;
4800     }
4802     if ($md_block->{"type"} eq "quote") {
4803       if (!$md_block->{"interrupted"}) {
4804         $line =~ s/^[ ]*>[ ]?//;
4805         push @{$md_block->{"lines"}}, $line;
4806         next OUTER;
4807       }
4808     } elsif ($md_block->{"type"} eq "li") {
4809       my $marker = $md_block->{"marker"};
4810       if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4811         my $indentation = $1;
4812         if ($md_block->{"indentation"} ne $indentation) {
4813           push @{$md_block->{"lines"}}, $line;
4814         } else {
4815           my $lines = $3;
4816           my $ordered = $md_block->{"ordered"};
4817           $lines =~ s/^[ ]{0,4}//;
4818           $md_block->{"last"} = 0;
4819           push @md_blocks, $md_block;
4820           $md_block = { type => "li",
4821                         ordered => $ordered,
4822                         indentation => $indentation,
4823                         marker => $marker,
4824                         first => 0,
4825                         last => 1,
4826                         lines => [ $lines ] };
4827         }
4828         next OUTER;
4829       }
4831       if ($md_block->{"interrupted"}) {
4832         if ($first_char eq " ") {
4833           push @{$md_block->{"lines"}}, "";
4834           $line =~ s/^[ ]{0,4}//;
4835           push @{$md_block->{"lines"}}, $line;
4836           $md_block->{"interrupted"} = 0;
4837           next OUTER;
4838         }
4839       } else {
4840         $line =~ s/^[ ]{0,4}//;
4841         push @{$md_block->{"lines"}}, $line;
4842         next OUTER;
4843       }
4844     }
4846     # indentation sensitive types
4848     if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4849       # atx heading (#)
4850       push @md_blocks, $md_block;
4852       $md_block = { type => "heading",
4853                     text => $2,
4854                     id => $3,
4855                     lines => [],
4856                     level => length($1) };
4858       next OUTER;
4859     } elsif ($line =~ /^={4,}[ \t]*$/) {
4860       # setext heading (====)
4862       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4863         push @md_blocks, $md_block;
4864         $md_block->{"type"} = "heading";
4865         $md_block->{"lines"} = [];
4866         $md_block->{"level"} = 1;
4867       }
4869       next OUTER;
4870     } elsif ($line =~ /^-{4,}[ \t]*$/) {
4871       # setext heading (-----)
4873       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4874         push @md_blocks, $md_block;
4875         $md_block->{"type"} = "heading";
4876         $md_block->{"lines"} = [];
4877         $md_block->{"level"} = 2;
4878       }
4880       next OUTER;
4881     } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4882       # code
4883       $md_block->{"interrupted"} = 1;
4884       push @md_blocks, $md_block;
4885       $md_block = { type => "code",
4886                     language => $1,
4887                     lines => [] };
4888       next OUTER;
4889     }
4891     # indentation insensitive types
4892     if ($line =~ /^[ ]*<!DOCTYPE/) {
4893       push @md_blocks, $md_block;
4895       $md_block = { type   => "markup",
4896                     text   => $deindented_line,
4897                     start  => "<",
4898                     end    => ">",
4899                     closed => 0,
4900                     depth  => 0 };
4902     } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4903       # markup, including <?xml version="1.0"?>
4904       my $tag = $1;
4905       my $is_self_closing = defined($2);
4906       # FIXME: why do we need to skip https? here, if we generalize this to all
4907       # uri schemes we get parsing errors
4908       if (! $MD_TEXT_LEVEL_ELEMENTS{$tag} && $tag !~ /^https?/) {
4909         push @md_blocks, $md_block;
4911         if ($is_self_closing) {
4912           $md_block = { type => "self-closing tag",
4913                         text => $deindented_line };
4914           $is_self_closing = 0;
4915           next OUTER;
4916         }
4918         $md_block = { type   => "markup",
4919                       text   => $deindented_line,
4920                       start  => "<" . $tag . ">",
4921                       end    => "</" . $tag . ">",
4922                       closed => 0,
4923                       depth  => 0 };
4924         if ($deindented_line =~ /<\/$tag>/) {
4925           $md_block->{"closed"} = 1;
4926         }
4927         next OUTER;
4928       }
4929     } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
4930       # li
4931       push @md_blocks, $md_block;
4932       my $lines = $2;
4933       my $indentation = $1;
4934       $lines =~ s/^[ ]{0,4}//;
4935       $md_block = { type => "li",
4936                     ordered => 0,
4937                     indentation => $indentation,
4938                     marker => "[*+-]",
4939                     first => 1,
4940                     last => 1,
4941                     lines => [ $lines ] };
4942       next OUTER;
4943     } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
4944       push @md_blocks, $md_block;
4945       $md_block = { type => "quote",
4946                     lines => [ $1 ] };
4947       next OUTER;
4948     }
4950     # list item
4952     if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
4953       push @md_blocks, $md_block;
4954       my $lines = $2;
4955       my $indentation = $1;
4956       $lines =~ s/^[ ]{0,4}//;
4958       $md_block = { type => "li",
4959                     ordered => 1,
4960                     indentation => $indentation,
4961                     marker => "\\d+[.]",
4962                     first => 1,
4963                     last => 1,
4964                     lines => [ $lines ] };
4966       next;
4967     }
4969     # paragraph
4970     if ($md_block->{"type"} eq "paragraph") {
4971       if ($md_block->{"interrupted"}) {
4972         push @md_blocks, $md_block;
4973         $md_block = { type => "paragraph",
4974                       interrupted => 0,
4975                       text => $line };
4976       } else {
4977         $md_block->{"text"} .= "\n" . $line;
4978       }
4979     } else {
4980       push @md_blocks, $md_block;
4981       $md_block = { type => "paragraph",
4982                     text => $line };
4983     }
4984   }
4986   push @md_blocks, $md_block;
4988   shift @md_blocks;
4990   return @md_blocks;
4993 sub MarkDownParseSpanElementsInner {
4994   my ($text, $markersref) = @_;
4995   my $markup = "";
4996   my %markers = map { $_ => 1 } @$markersref;
4998   while ($text ne "") {
4999     my $closest_marker = "";
5000     my $closest_marker_index = 0;
5001     my $closest_marker_position = -1;
5002     my $text_marker = "";
5003     my $i = 0;
5004     my $offset = 0;
5005     my @markers_rest;
5006     my $marker;
5007     my $use;
5009     while ( ($marker, $use) = each %markers ) {
5010       my $marker_position;
5012       if (!$use) {
5013         next;
5014       }
5016       $marker_position = index ($text, $marker);
5018       if ($marker_position < 0) {
5019         $markers{$marker} = 0;
5020         next;
5021       }
5023       if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5024         $closest_marker = $marker;
5025         $closest_marker_index = $i;
5026         $closest_marker_position = $marker_position;
5027       }
5028     }
5030     if ($closest_marker_position >= 0) {
5031       $text_marker = substr ($text, $closest_marker_position);
5032     }
5034     if ($text_marker eq "") {
5035       $markup .= $text;
5036       $text = "";
5037       next; # last
5038     }
5040     $markup .= substr ($text, 0, $closest_marker_position);
5041     $text = substr ($text, $closest_marker_position);
5042     @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5044     if ($closest_marker eq "![" || $closest_marker eq "[") {
5045       my %element;
5047       if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5048         my $remaining_text;
5050         %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5051                      "a" => $1 );
5053         $offset = length ($&);
5054         if ($element{"!"}) {
5055           $offset++;
5056         }
5058         $remaining_text = substr ($text, $offset);
5059         if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5060           $element{"»"} = $1;
5061           if (defined ($2)) {
5062             $element{"#"} = $2;
5063           }
5064           $offset += length ($&);
5065         } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5066           $element{"ref"} = $1;
5067           $offset += length ($&);
5068         } else {
5069           undef %element;
5070         }
5071       }
5073       if (%element) {
5074         if ($element{"»"}) {
5075           $element{"»"} =~ s/&/&amp;/g;
5076           $element{"»"} =~ s/</&lt;/g;
5077         }
5078         if ($element{"!"}) {
5079           $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5081           if (defined ($element{"a"})) {
5082             $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5083           }
5085           $markup .= "</inlinemediaobject>";
5086         } elsif ($element{"ref"}) {
5087           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5088           $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5090           if (defined ($element{"#"})) {
5091             # title attribute not supported
5092           }
5094           $markup .= ">" . $element{"a"} . "</link>";
5095         } else {
5096           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5097           $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5099           if (defined ($element{"#"})) {
5100             # title attribute not supported
5101           }
5103           $markup .= ">" . $element{"a"} . "</ulink>";
5104         }
5105       } else {
5106         $markup .= $closest_marker;
5107         if ($closest_marker eq "![") {
5108           $offset = 2;
5109         } else {
5110           $offset = 1;
5111         }
5112       }
5113     } elsif ($closest_marker eq "<") {
5114       if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5115         my $element_url = $1;
5116         $element_url =~ s/&/&amp;/g;
5117         $element_url =~ s/</&lt;/g;
5119         $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5120         $offset = length ($&);
5121       } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5122         $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5123         $offset = length ($&);
5124       } elsif ($text =~ /^<[^>]+?>/) {
5125         $markup .= $&;
5126         $offset = length ($&);
5127       } else {
5128         $markup .= "&lt;";
5129         $offset = 1;
5130       }
5131     } elsif ($closest_marker eq "\\") {
5132       my $special_char = substr ($text, 1, 1);
5133       if ($MD_ESCAPABLE_CHARS{$special_char} ||
5134           $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5135         $markup .= $special_char;
5136         $offset = 2;
5137       } else {
5138         $markup .= "\\";
5139         $offset = 1;
5140       }
5141     } elsif ($closest_marker eq "`") {
5142       if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5143         my $element_text = $2;
5144         $markup .= "<literal>" . $element_text . "</literal>";
5145         $offset = length ($&);
5146       } else {
5147         $markup .= "`";
5148         $offset = 1;
5149       }
5150     } elsif ($closest_marker eq "@") {
5151       # Convert '@param()'
5152       # FIXME: we could make those also links ($symbol.$2), but that would be less
5153       # useful as the link target is a few lines up or down
5154       if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5155         $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5156         $offset = length ($&);
5157       } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5158         # Convert '@param', but not '\@param'.
5159         $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5160         $offset = length ($&);
5161       } elsif ($text =~ /^\\\@/) {
5162         $markup .= "\@";
5163         $offset = length ($&);
5164       } else {
5165         $markup .= "@";
5166         $offset = 1;
5167       }
5168     } elsif ($closest_marker eq "#") {
5169       if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5170         # handle #Object.func()
5171         $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5172         $offset = length ($&);
5173       } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5174         # Convert '#symbol', but not '\#symbol'.
5175         $markup .= $1 . &MakeHashXRef ($2, "type");
5176         $offset = length ($&);
5177       } elsif ($text =~ /^\\#/) {
5178         $markup .= "#";
5179         $offset = length ($&);
5180       } else {
5181         $markup .= "#";
5182         $offset = 1;
5183       }
5184     } elsif ($closest_marker eq "%") {
5185       if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5186         # Convert '%constant', but not '\%constant'.
5187         # Also allow negative numbers, e.g. %-1.
5188         $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5189         $offset = length ($&);
5190       } elsif ($text =~ /^\\%/) {
5191         $markup .= "\%";
5192         $offset = length ($&);
5193       } else {
5194         $markup .= "%";
5195         $offset = 1;
5196       }
5197     }
5199     if ($offset > 0) {
5200       $text = substr ($text, $offset);
5201     }
5202   }
5204   return $markup;
5207 sub MarkDownParseSpanElements {
5208   my ($text) = @_;
5209   my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5211   $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5213   # Convert 'function()' or 'macro()'.
5214   # if there is abc_*_def() we don't want to make a link to _def()
5215   # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5216   $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5218   return $text;
5221 sub ReplaceEntities {
5222   my ($text, $symbol) = @_;
5223   my $warn = "";
5224   my @entities = ( [ "&lt;", "<" ],
5225                    [ "&gt;", ">" ],
5226                    [ "&ast;", "*" ],
5227                    [ "&num;", "#" ],
5228                    [ "&percnt;", "%"],
5229                    [ "&colon;", ":" ],
5230                    [ "&quot;", "\"" ],
5231                    [ "&apos;", "'" ],
5232                    [ "&nbsp;", " " ],
5233                    [ "&amp;", "&" ] ); # Do this last, or the others get messed up.
5234   my $i;
5236   # Expand entities in <programlisting> even inside CDATA since
5237   # we changed the definition of |[ to add CDATA
5238   for ($i = 0; $i <= $#entities; $i++) {
5239     if ($text =~ s/$entities[$i][0]/$entities[$i][1]/g) {
5240       # don't warn about &ast; since it is expected to be present
5241       # for C-style comments
5242       if ($entities[$i][0] ne "&ast;") {
5243         $warn .= "$entities[$i][0] ";
5244       }
5245     }
5246   }
5248   if ($warn ne "") {
5249     chomp $warn;
5250     &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
5251                  "Deprecated entities found in documentation for $symbol: $warn");
5252   }
5254   return $text;
5257 sub MarkDownOutputDocBook {
5258   my ($blocksref, $symbol, $context) = @_;
5259   my $output = "";
5260   my $block;
5261   my @blocks = @$blocksref;
5263   foreach $block (@blocks) {
5264     my $text;
5265     my $title;
5267     if ($block->{"type"} eq "paragraph") {
5268       $text = &MarkDownParseSpanElements ($block->{"text"});
5269       if ($context eq "li" && $output eq "") {
5270         if ($block->{"interrupted"}) {
5271           $output .= "\n"."<para>".$text."</para>"."\n";
5272         } else {
5273           $output .= "<para>".$text."</para>";
5274           if ($#blocks > 0) {
5275             $output .= "\n";
5276           }
5277         }
5278       } else {
5279         $output .= "<para>".$text."</para>"."\n";
5280       }
5282     } elsif ($block->{"type"} eq "heading") {
5283       my $tag;
5285       $title = &MarkDownParseSpanElements ($block->{"text"});
5287       if ($block->{"level"} == 1) {
5288         $tag = "refsect2";
5289       } else {
5290         $tag = "refsect3";
5291       }
5293       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5294       if (defined ($block->{"id"})) {
5295         $output .= "<" . $tag . " id=\"" . $block->{"id"} . "\">";
5296       } else {
5297         $output .= "<" . $tag . ">";
5298       }
5300       $output .= "<title>" . $title . "</title>" . $text . "</" . $tag . ">\n";
5301     } elsif ($block->{"type"} eq "li") {
5302       my $tag = "itemizedlist";
5304       if ($block->{"first"}) {
5305         if ($block->{"ordered"}) {
5306           $tag = "orderedlist";
5307         }
5308         $output .= "<".$tag.">\n";
5309       }
5311       if ($block->{"interrupted"}) {
5312         push @{$block->{"lines"}}, "";
5313       }
5315       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5316       $output .= "<listitem>".$text."</listitem>\n";
5317       if ($block->{"last"}) {
5318         if ($block->{"ordered"}) {
5319           $tag = "orderedlist";
5320         }
5321         $output .= "</".$tag.">\n";
5322       }
5323     } elsif ($block->{"type"} eq "quote") {
5324       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5325       $output .= "<blockquote>\n" . $text . "</blockquote>\n";
5326     } elsif ($block->{"type"} eq "code") {
5327       if ($block->{"language"}) {
5328         $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5329       } else {
5330         $output .= "<informalexample><programlisting><![CDATA[\n";
5331       }
5332       foreach (@{$block->{"lines"}}) {
5333         $output .= &ReplaceEntities ($_, $symbol) . "\n";
5334       }
5335       $output .= "]]></programlisting></informalexample>\n";
5336     } elsif ($block->{"type"} eq "markup") {
5337       $text = &ExpandAbbreviations($symbol, $block->{"text"});
5338       $output .= $text."\n";
5339     } else {
5340       $output .= $block->{"text"}."\n";
5341     }
5342   }
5344   return $output;
5347 sub MarkDownParseLines {
5348   my ($linesref, $symbol, $context) = @_;
5349   my $output;
5350   my @lines = @$linesref;
5351   my @blocks;
5353   @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5354   $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5356   return $output;
5359 sub MarkDownParse {
5360   my ($text, $symbol) = @_;
5361   my @lines;
5363   # take out some variability in line endings
5364   $text =~ s%\r\n%\n%g;
5365   $text =~ s%\r%\n%g;
5367   # split lines
5368   @lines = split("\n", $text);
5369   $text = MarkDownParseLines(\@lines, $symbol, "");
5371   return $text;
5374 #############################################################################
5375 # LIBRARY FUNCTIONS -        These functions are used in both gtkdoc-mkdb and
5376 #                        gtkdoc-mktmpl and should eventually be moved to a
5377 #                        separate library.
5378 #############################################################################
5380 #############################################################################
5381 # Function    : ReadDeclarationsFile
5382 # Description : This reads in a file containing the function/macro/enum etc.
5383 #                declarations.
5385 #                Note that in some cases there are several declarations with
5386 #                the same name, e.g. for conditional macros. In this case we
5387 #                set a flag in the %DeclarationConditional hash so the
5388 #                declaration is not shown in the docs.
5390 #                If a macro and a function have the same name, e.g. for
5391 #                gtk_object_ref, the function declaration takes precedence.
5393 #                Some opaque structs are just declared with 'typedef struct
5394 #                _name name;' in which case the declaration may be empty.
5395 #                The structure may have been found later in the header, so
5396 #                that overrides the empty declaration.
5398 # Arguments   : $file - the declarations file to read
5399 #                $override - if declarations in this file should override
5400 #                        any current declaration.
5401 #############################################################################
5403 sub ReadDeclarationsFile {
5404     my ($file, $override) = @_;
5406     if ($override == 0) {
5407         %Declarations = ();
5408         %DeclarationTypes = ();
5409         %DeclarationConditional = ();
5410         %DeclarationOutput = ();
5411     }
5413     open (INPUT, $file)
5414         || die "Can't open $file: $!";
5415     my $declaration_type = "";
5416     my $declaration_name;
5417     my $declaration;
5418     my $is_deprecated = 0;
5419     while (<INPUT>) {
5420         if (!$declaration_type) {
5421             if (m/^<([^>]+)>/) {
5422                 $declaration_type = $1;
5423                 $declaration_name = "";
5424                 @TRACE@("Found declaration: $declaration_type\n");
5425                 $declaration = "";
5426             }
5427         } else {
5428             if (m%^<NAME>(.*)</NAME>%) {
5429                 $declaration_name = $1;
5430             } elsif (m%^<DEPRECATED/>%) {
5431                 $is_deprecated = 1;
5432             } elsif (m%^</$declaration_type>%) {
5433                 @TRACE@("Found end of declaration: $declaration_name\n");
5434                 # Check that the declaration has a name
5435                 if ($declaration_name eq "") {
5436                     &LogWarning ($file, $., "$declaration_type has no name.\n");
5437                 }
5439                 # If the declaration is an empty typedef struct _XXX XXX
5440                 # set the flag to indicate the struct has a typedef.
5441                 if ($declaration_type eq 'STRUCT'
5442                     && $declaration =~ m/^\s*$/) {
5443                     @TRACE@("Struct has typedef: $declaration_name\n");
5444                     $StructHasTypedef{$declaration_name} = 1;
5445                 }
5447                 # Check if the symbol is already defined.
5448                 if (defined ($Declarations{$declaration_name})
5449                     && $override == 0) {
5450                     # Function declarations take precedence.
5451                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5452                         # Ignore it.
5453                     } elsif ($declaration_type eq 'FUNCTION') {
5454                         if ($is_deprecated) {
5455                             $Deprecated{$declaration_name} = "";
5456                         }
5457                         $Declarations{$declaration_name} = $declaration;
5458                         $DeclarationTypes{$declaration_name} = $declaration_type;
5459                     } elsif ($DeclarationTypes{$declaration_name}
5460                               eq $declaration_type) {
5461                         # If the existing declaration is empty, or is just a
5462                         # forward declaration of a struct, override it.
5463                         if ($declaration_type eq 'STRUCT') {
5464                             if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5465                                 if ($is_deprecated) {
5466                                     $Deprecated{$declaration_name} = "";
5467                                 }
5468                                 $Declarations{$declaration_name} = $declaration;
5469                             } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5470                                 # Ignore an empty or forward declaration.
5471                             } else {
5472                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5473                             }
5474                         } else {
5475                             # set flag in %DeclarationConditional hash for
5476                             # multiply defined macros/typedefs.
5477                             $DeclarationConditional{$declaration_name} = 1;
5478                         }
5479                     } else {
5480                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5481                     }
5482                 } else {
5483                     if ($is_deprecated) {
5484                         $Deprecated{$declaration_name} = "";
5485                     }
5486                     $Declarations{$declaration_name} = $declaration;
5487                     $DeclarationTypes{$declaration_name} = $declaration_type;
5488                 }
5490                 $declaration_type = "";
5491                 $is_deprecated = 0;
5492             } else {
5493                 $declaration .= $_;
5494             }
5495         }
5496     }
5497     close (INPUT);
5501 #############################################################################
5502 # Function    : ReadSignalsFile
5503 # Description : This reads in an existing file which contains information on
5504 #                all GTK signals. It creates the arrays @SignalNames and
5505 #                @SignalPrototypes containing info on the signals. The first
5506 #                line of the SignalPrototype is the return type of the signal
5507 #                handler. The remaining lines are the parameters passed to it.
5508 #                The last parameter, "gpointer user_data" is always the same
5509 #                so is not included.
5510 # Arguments   : $file - the file containing the signal handler prototype
5511 #                        information.
5512 #############################################################################
5514 sub ReadSignalsFile {
5515     my ($file) = @_;
5517     my $in_signal = 0;
5518     my $signal_object;
5519     my $signal_name;
5520     my $signal_returns;
5521     my $signal_flags;
5522     my $signal_prototype;
5524     # Reset the signal info.
5525     @SignalObjects = ();
5526     @SignalNames = ();
5527     @SignalReturns = ();
5528     @SignalFlags = ();
5529     @SignalPrototypes = ();
5531     if (! -f $file) {
5532         return;
5533     }
5534     if (!open (INPUT, $file)) {
5535         warn "Can't open $file - skipping signals\n";
5536         return;
5537     }
5538     while (<INPUT>) {
5539         if (!$in_signal) {
5540             if (m/^<SIGNAL>/) {
5541                 $in_signal = 1;
5542                 $signal_object = "";
5543                 $signal_name = "";
5544                 $signal_returns = "";
5545                 $signal_prototype = "";
5546             }
5547         } else {
5548             if (m/^<NAME>(.*)<\/NAME>/) {
5549                 $signal_name = $1;
5550                 if ($signal_name =~ m/^(.*)::(.*)$/) {
5551                     $signal_object = $1;
5552                     ($signal_name = $2) =~ s/_/-/g;
5553                     @TRACE@("Found signal: $signal_name\n");
5554                 } else {
5555                     &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5556                 }
5557             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5558                 $signal_returns = $1;
5559             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5560                 $signal_flags = $1;
5561             } elsif (m%^</SIGNAL>%) {
5562                 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5563                 push (@SignalObjects, $signal_object);
5564                 push (@SignalNames, $signal_name);
5565                 push (@SignalReturns, $signal_returns);
5566                 push (@SignalFlags, $signal_flags);
5567                 push (@SignalPrototypes, $signal_prototype);
5568                 $in_signal = 0;
5569             } else {
5570                 $signal_prototype .= $_;
5571             }
5572         }
5573     }
5574     close (INPUT);
5578 #############################################################################
5579 # Function    : ReadTemplateFile
5580 # Description : This reads in the manually-edited documentation file
5581 #               corresponding to the file currently being created, so we can
5582 #               insert the documentation at the appropriate places.
5583 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5584 #               is a hash of arrays.
5585 #               NOTE: This function is duplicated in gtkdoc-mktmpl (but
5586 #               slightly different).
5587 # Arguments   : $docsfile - the template file to read in.
5588 #               $skip_unused_params - 1 if the unused parameters should be
5589 #                 skipped.
5590 #############################################################################
5592 sub ReadTemplateFile {
5593     my ($docsfile, $skip_unused_params) = @_;
5595     my $template = "$docsfile.sgml";
5596     if (! -f $template) {
5597         @TRACE@("File doesn't exist: $template\n");
5598         return 0;
5599     }
5601     # start with empty hashes, we merge the source comment for each file
5602     # afterwards
5603     %SymbolDocs = ();
5604     %SymbolTypes = ();
5605     %SymbolParams = ();
5607     my $current_type = "";        # Type of symbol being read.
5608     my $current_symbol = "";        # Name of symbol being read.
5609     my $symbol_doc = "";                # Description of symbol being read.
5610     my @params;                        # Parameter names and descriptions of current
5611                                 #   function/macro/function typedef.
5612     my $current_param = -1;        # Index of parameter currently being read.
5613                                 #   Note that the param array contains pairs
5614                                 #   of param name & description.
5615     my $in_unused_params = 0;        # True if we are reading in the unused params.
5616     my $in_deprecated = 0;
5617     my $in_since = 0;
5618     my $in_stability = 0;
5620     open (DOCS, "$template")
5621         || die "Can't open $template: $!";
5623     @TRACE@("reading template $template");
5625     while (<DOCS>) {
5626         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5627             my $type = $1;
5628             my $symbol = $2;
5629             if ($symbol eq "Title"
5630                 || $symbol eq "Short_Description"
5631                 || $symbol eq "Long_Description"
5632                 || $symbol eq "See_Also"
5633                 || $symbol eq "Stability_Level"
5634                 || $symbol eq "Include"
5635                 || $symbol eq "Image") {
5637                 $symbol = $docsfile . ":" . $symbol;
5638             }
5640             @TRACE@("Found symbol: $symbol\n");
5641             # Remember file and line for the symbol
5642             $SymbolSourceFile{$symbol} = $template;
5643             $SymbolSourceLine{$symbol} = $.;
5645             # Store previous symbol, but remove any trailing blank lines.
5646             if ($current_symbol ne "") {
5647                 $symbol_doc =~ s/\s+$//;
5648                 $SymbolTypes{$current_symbol} = $current_type;
5649                 $SymbolDocs{$current_symbol} = $symbol_doc;
5651                 # Check that the stability level is valid.
5652                 if ($StabilityLevel{$current_symbol}) {
5653                     $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5654                 }
5656                 if ($current_param >= 0) {
5657                     $SymbolParams{$current_symbol} = [ @params ];
5658                 } else {
5659                     # Delete any existing params in case we are overriding a
5660                     # previously read template.
5661                     delete $SymbolParams{$current_symbol};
5662                 }
5663             }
5664             $current_type = $type;
5665             $current_symbol = $symbol;
5666             $current_param = -1;
5667             $in_unused_params = 0;
5668             $in_deprecated = 0;
5669             $in_since = 0;
5670             $in_stability = 0;
5671             $symbol_doc = "";
5672             @params = ();
5674         } elsif (m/^<!-- # Unused Parameters # -->/) {
5675             @TRACE@("Found unused parameters\n");
5676             $in_unused_params = 1;
5677             next;
5679         } elsif ($in_unused_params && $skip_unused_params) {
5680             # When outputting the DocBook we skip unused parameters.
5681             @TRACE@("Skipping unused param: $_");
5682             next;
5684         } else {
5685             # Check if param found. Need to handle "..." and "format...".
5686             if (s/^\@([\w\.]+):\040?//) {
5687                 my $param_name = $1;
5688                 my $param_desc = $_;
5689                 # Allow variations of 'Returns'
5690                 if ($param_name =~ m/^[Rr]eturns?$/) {
5691                     $param_name = "Returns";
5692                 }
5693                 # Allow varargs variations
5694                 if ($param_name =~ m/^.*\.\.\.$/) {
5695                     $param_name = "...";
5696                 }
5698                 # strip trailing whitespaces and blank lines
5699                 s/\s+\n$/\n/m;
5700                 s/\n+$/\n/sm;
5701                 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5703                 if ($param_name eq "Deprecated") {
5704                     $in_deprecated = 1;
5705                     $Deprecated{$current_symbol} = $_;
5706                 } elsif ($param_name eq "Since") {
5707                     $in_since = 1;
5708                     chomp;
5709                     $Since{$current_symbol} = $_;
5710                 } elsif ($param_name eq "Stability") {
5711                     $in_stability = 1;
5712                     $StabilityLevel{$current_symbol} = $_;
5713                 } else {
5714                     push (@params, $param_name);
5715                     push (@params, $param_desc);
5716                     $current_param += $PARAM_FIELD_COUNT;
5717                 }
5718             } else {
5719                 # strip trailing whitespaces and blank lines
5720                 s/\s+\n$/\n/m;
5721                 s/\n+$/\n/sm;
5723                 if (!m/^\s+$/) {
5724                     if ($in_deprecated) {
5725                         $Deprecated{$current_symbol} .= $_;
5726                     } elsif ($in_since) {
5727                         &LogWarning ($template, $., "multi-line since docs found");
5728                         #$Since{$current_symbol} .= $_;
5729                     } elsif ($in_stability) {
5730                         $StabilityLevel{$current_symbol} .= $_;
5731                     } elsif ($current_param >= 0) {
5732                         $params[$current_param] .= $_;
5733                     } else {
5734                         $symbol_doc .= $_;
5735                     }
5736                 }
5737             }
5738         }
5739     }
5741     # Remember to finish the current symbol doccs.
5742     if ($current_symbol ne "") {
5744         $symbol_doc =~ s/\s+$//;
5745         $SymbolTypes{$current_symbol} = $current_type;
5746         $SymbolDocs{$current_symbol} = $symbol_doc;
5748         # Check that the stability level is valid.
5749         if ($StabilityLevel{$current_symbol}) {
5750             $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5751         }
5753         if ($current_param >= 0) {
5754             $SymbolParams{$current_symbol} = [ @params ];
5755         } else {
5756             # Delete any existing params in case we are overriding a
5757             # previously read template.
5758             delete $SymbolParams{$current_symbol};
5759         }
5760     }
5762     close (DOCS);
5763     return 1;
5767 #############################################################################
5768 # Function    : ReadObjectHierarchy
5769 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5770 #               the GtkObject subclasses described in this module (and their
5771 #               ancestors).
5772 #               It places them in the @Objects array, and places their level
5773 #               in the object hierarchy in the @ObjectLevels array, at the
5774 #               same index. GtkObject, the root object, has a level of 1.
5776 #               This also generates tree_index.sgml as it goes along.
5778 # Arguments   : none
5779 #############################################################################
5781 sub ReadObjectHierarchy {
5782     @Objects = ();
5783     @ObjectLevels = ();
5785     if (! -f $OBJECT_TREE_FILE) {
5786         return;
5787     }
5788     if (!open (INPUT, $OBJECT_TREE_FILE)) {
5789         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5790         return;
5791     }
5793     # Only emit objects if they are supposed to be documented, or if
5794     # they have documented children. To implement this, we maintain a
5795     # stack of pending objects which will be emitted if a documented
5796     # child turns up.
5797     my @pending_objects = ();
5798     my @pending_levels = ();
5799     my $root;
5800     my @tree = ();
5801     while (<INPUT>) {
5802         if (m/\S+/) {
5803             my $object = $&;
5804             my $level = (length($`)) / 2 + 1;
5805             my $xref = "";
5807             if ($level == 1) {
5808                 $root = $object;
5809             }
5811             while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5812                 my $pobject = pop(@pending_objects);
5813                 my $plevel = pop(@pending_levels);
5814             }
5816             push (@pending_objects, $object);
5817             push (@pending_levels, $level);
5819             if (exists($KnownSymbols{$object})) {
5820                 while ($#pending_levels >= 0) {
5821                     $object = shift @pending_objects;
5822                     $level = shift @pending_levels;
5823                     $xref = &MakeXRef ($object);
5825                     push (@tree, ' ' x ($level * 4) . "$xref");
5826                     push (@Objects, $object);
5827                     push (@ObjectLevels, $level);
5828                     $ObjectRoots{$object} = $root;
5829                 }
5830             }
5831             #else {
5832             #    LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5833             #}
5834         }
5835     }
5836     close (INPUT);
5838     # FIXME: use $OUTPUT_FORMAT
5839     # my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.$OUTPUT_FORMAT";
5840     my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml";
5841     my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new";
5843     open (OUTPUT, ">$new_tree_index")
5844         || die "Can't create $new_tree_index: $!";
5846     if ($OUTPUT_FORMAT eq "xml") {
5847         my $tree_header = $doctype_header;
5849         $tree_header =~ s/<!DOCTYPE \w+/<!DOCTYPE screen/;
5850         print (OUTPUT "$tree_header");
5851     }
5852     print (OUTPUT "<screen>\n" . &AddTreeLineArt(\@tree) . "\n</screen>\n");
5853     close (OUTPUT);
5855     &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5857     &OutputObjectList;
5860 #############################################################################
5861 # Function    : ReadInterfaces
5862 # Description : This reads in the $MODULE.interfaces file.
5864 # Arguments   : none
5865 #############################################################################
5867 sub ReadInterfaces {
5868     %Interfaces = ();
5870     if (! -f $INTERFACES_FILE) {
5871         return;
5872     }
5873     if (!open (INPUT, $INTERFACES_FILE)) {
5874         warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5875         return;
5876     }
5878     while (<INPUT>) {
5879        chomp;
5880        my ($object, @ifaces) = split;
5881        if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5882            my @knownIfaces = ();
5884            # filter out private interfaces, but leave foreign interfaces
5885            foreach my $iface (@ifaces) {
5886                if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5887                    push (@knownIfaces, $iface);
5888                }
5889              }
5891            $Interfaces{$object} = join(' ', @knownIfaces);
5892        }
5893     }
5894     close (INPUT);
5897 #############################################################################
5898 # Function    : ReadPrerequisites
5899 # Description : This reads in the $MODULE.prerequisites file.
5901 # Arguments   : none
5902 #############################################################################
5904 sub ReadPrerequisites {
5905     %Prerequisites = ();
5907     if (! -f $PREREQUISITES_FILE) {
5908         return;
5909     }
5910     if (!open (INPUT, $PREREQUISITES_FILE)) {
5911         warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
5912         return;
5913     }
5915     while (<INPUT>) {
5916        chomp;
5917        my ($iface, @prereqs) = split;
5918        if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
5919            my @knownPrereqs = ();
5921            # filter out private prerequisites, but leave foreign prerequisites
5922            foreach my $prereq (@prereqs) {
5923                if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
5924                   push (@knownPrereqs, $prereq);
5925                }
5926            }
5928            $Prerequisites{$iface} = join(' ', @knownPrereqs);
5929        }
5930     }
5931     close (INPUT);
5934 #############################################################################
5935 # Function    : ReadArgsFile
5936 # Description : This reads in an existing file which contains information on
5937 #                all GTK args. It creates the arrays @ArgObjects, @ArgNames,
5938 #                @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
5939 #               on the args.
5940 # Arguments   : $file - the file containing the arg information.
5941 #############################################################################
5943 sub ReadArgsFile {
5944     my ($file) = @_;
5946     my $in_arg = 0;
5947     my $arg_object;
5948     my $arg_name;
5949     my $arg_type;
5950     my $arg_flags;
5951     my $arg_nick;
5952     my $arg_blurb;
5953     my $arg_default;
5954     my $arg_range;
5956     # Reset the args info.
5957     @ArgObjects = ();
5958     @ArgNames = ();
5959     @ArgTypes = ();
5960     @ArgFlags = ();
5961     @ArgNicks = ();
5962     @ArgBlurbs = ();
5963     @ArgDefaults = ();
5964     @ArgRanges = ();
5966     if (! -f $file) {
5967         return;
5968     }
5969     if (!open (INPUT, $file)) {
5970         warn "Can't open $file - skipping args\n";
5971         return;
5972     }
5973     while (<INPUT>) {
5974         if (!$in_arg) {
5975             if (m/^<ARG>/) {
5976                 $in_arg = 1;
5977                 $arg_object = "";
5978                 $arg_name = "";
5979                 $arg_type = "";
5980                 $arg_flags = "";
5981                 $arg_nick = "";
5982                 $arg_blurb = "";
5983                 $arg_default = "";
5984                 $arg_range = "";
5985             }
5986         } else {
5987             if (m/^<NAME>(.*)<\/NAME>/) {
5988                 $arg_name = $1;
5989                 if ($arg_name =~ m/^(.*)::(.*)$/) {
5990                     $arg_object = $1;
5991                     ($arg_name = $2) =~ s/_/-/g;
5992                     @TRACE@("Found arg: $arg_name\n");
5993                 } else {
5994                     &LogWarning ($file, $., "Invalid argument name: $arg_name");
5995                 }
5996             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
5997                 $arg_type = $1;
5998             } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
5999                 $arg_range = $1;
6000             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6001                 $arg_flags = $1;
6002             } elsif (m/^<NICK>(.*)<\/NICK>/) {
6003                 $arg_nick = $1;
6004             } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6005                 $arg_blurb = $1;
6006                 if ($arg_blurb eq "(null)") {
6007                   $arg_blurb = "";
6008                   &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6009                 }
6010             } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6011                 $arg_default = $1;
6012             } elsif (m%^</ARG>%) {
6013                 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6014                 push (@ArgObjects, $arg_object);
6015                 push (@ArgNames, $arg_name);
6016                 push (@ArgTypes, $arg_type);
6017                 push (@ArgRanges, $arg_range);
6018                 push (@ArgFlags, $arg_flags);
6019                 push (@ArgNicks, $arg_nick);
6020                 push (@ArgBlurbs, $arg_blurb);
6021                 push (@ArgDefaults, $arg_default);
6022                 $in_arg = 0;
6023             }
6024         }
6025     }
6026     close (INPUT);
6029 #############################################################################
6030 # Function    : AddTreeLineArt
6031 # Description : Add unicode lineart to a pre-indented string array and returns
6032 #               it as as multiline string.
6033 # Arguments   : @tree - array of indented strings.
6034 #############################################################################
6036 sub AddTreeLineArt {
6037   my @tree = @{$_[0]};
6038   my $i;
6039   my $j;
6040   my $indent;
6041   
6042   # iterate bottom up over the tree 
6043   for ($i = $#tree; $i >= 0; $i--) {
6044     # count leading spaces
6045     $tree[$i] =~ /^([^<A-Za-z]*)/;
6046     $indent = length( $1 );
6047     # replace with ╰───, if place of ╰ is not space insert ├
6048     if ($indent > 4) {
6049       if (substr($tree[$i],$indent-4,1) eq " ") {
6050         substr($tree[$i],$indent-4,4) = "--- ";
6051       } else {
6052         substr($tree[$i],$indent-4,4) = "+-- ";
6053       }
6054       # go lines up while space and insert |
6055       for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6056         substr($tree[$j],$indent-4,1) = '|';
6057       }
6058     }
6059   }
6060   
6061   my $res = join("\n", @tree);
6062   # unicode chars for: ╰──
6063   $res =~ s%---%<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>%g;
6064   # unicde chars for: ├──
6065   $res =~ s%\+--%<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>%g;
6066   # unicode char for: │
6067   $res =~ s%\|%<phrase role=\"lineart\">&#9474;</phrase>%g;
6068   
6069   return $res;
6073 #############################################################################
6074 # Function    : CheckIsObject
6075 # Description : Returns 1 if the given name is a GObject or a subclass.
6076 #                It uses the global @Objects array.
6077 #                Note that the @Objects array only contains classes in the
6078 #                current module and their ancestors - not all GObject classes.
6079 # Arguments   : $name - the name to check.
6080 #############################################################################
6082 sub CheckIsObject {
6083     my ($name) = @_;
6084     my $root = $ObjectRoots{$name};
6085     # Let GBoxed pass as an object here to get -struct appended to the id
6086     # and prevent conflicts with sections.
6087     return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6091 #############################################################################
6092 # Function    : MakeReturnField
6093 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6094 # Arguments   : $str - the string to pad.
6095 #############################################################################
6097 sub MakeReturnField {
6098     my ($str) = @_;
6100     return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6103 #############################################################################
6104 # Function    : GetSymbolSourceFile
6105 # Description : Get the filename where the symbol docs where taken from.
6106 # Arguments   : $symbol - the symbol name
6107 #############################################################################
6109 sub GetSymbolSourceFile {
6110     my ($symbol) = @_;
6112     if (defined($SourceSymbolSourceFile{$symbol})) {
6113         return $SourceSymbolSourceFile{$symbol};
6114     } elsif (defined($SymbolSourceFile{$symbol})) {
6115         return $SymbolSourceFile{$symbol};
6116     } else {
6117         return "";
6118     }
6121 #############################################################################
6122 # Function    : GetSymbolSourceLine
6123 # Description : Get the file line where the symbol docs where taken from.
6124 # Arguments   : $symbol - the symbol name
6125 #############################################################################
6127 sub GetSymbolSourceLine {
6128     my ($symbol) = @_;
6130     if (defined($SourceSymbolSourceLine{$symbol})) {
6131         return $SourceSymbolSourceLine{$symbol};
6132     } elsif (defined($SymbolSourceLine{$symbol})) {
6133         return $SymbolSourceLine{$symbol};
6134     } else {
6135         return 0;
6136     }