mkdb: use an indent of 4 for the hierarchy
[gtk-doc.git] / gtkdoc-mkdb.in
blobd77e589ab3b40fe4873806d01e84145658afb0cb
1 #!@PERL@ -w
2 # -*- cperl -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998  Damon Chaplin
6 #               2007,2008,2009  Stefan Kost
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #############################################################################
24 # Script      : gtkdoc-mkdb
25 # Description : This creates the DocBook files from the edited templates.
26 #############################################################################
28 use warnings;
29 use strict;
30 use Getopt::Long;
32 push @INC, '@PACKAGE_DATA_DIR@';
33 require "gtkdoc-common.pl";
35 # Options
37 # name of documentation module
38 my $MODULE;
39 my $TMPL_DIR;
40 my $SGML_OUTPUT_DIR;
41 my @SOURCE_DIRS;
42 my $SOURCE_SUFFIXES = "";
43 my $IGNORE_FILES = "";
44 my $PRINT_VERSION;
45 my $PRINT_HELP;
46 my $MAIN_SGML_FILE;
47 my $EXPAND_CONTENT_FILES = "";
48 my $INLINE_MARKUP_MODE;
49 my $DEFAULT_STABILITY;
50 my $DEFAULT_INCLUDES;
51 my $OUTPUT_FORMAT;
52 my $NAME_SPACE = "";
53 my $OUTPUT_ALL_SYMBOLS;
54 my $OUTPUT_SYMBOLS_WITHOUT_SINCE;
56 my %optctl = ('module' => \$MODULE,
57               'source-dir' => \@SOURCE_DIRS,
58               'source-suffixes' => \$SOURCE_SUFFIXES,
59               'ignore-files' => \$IGNORE_FILES,
60               'output-dir' => \$SGML_OUTPUT_DIR,
61               'tmpl-dir' => \$TMPL_DIR,
62               'version' => \$PRINT_VERSION,
63               'help' => \$PRINT_HELP,
64               'main-sgml-file' => \$MAIN_SGML_FILE,
65               'expand-content-files' => \$EXPAND_CONTENT_FILES,
66               'sgml-mode' => \$INLINE_MARKUP_MODE,
67               'xml-mode' => \$INLINE_MARKUP_MODE,
68               'default-stability' => \$DEFAULT_STABILITY,
69               'default-includes' => \$DEFAULT_INCLUDES,
70               'output-format' => \$OUTPUT_FORMAT,
71               'name-space' => \$NAME_SPACE,
72               'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
73               'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
74               );
75 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
76     "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version", "outputallsymbols",
77     "outputsymbolswithoutsince",
78     "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
79     "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
80     "output-format:s", "name-space:s");
82 if ($PRINT_VERSION) {
83     print "@VERSION@\n";
84     exit 0;
87 if (!$MODULE) {
88     $PRINT_HELP = 1;
91 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
92     && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
93     $PRINT_HELP = 1;
96 if ($PRINT_HELP) {
97     print <<EOF;
98 gtkdoc-mkdb version @VERSION@ - generate docbook files
100 --module=MODULE_NAME       Name of the doc module being parsed
101 --source-dir=DIRNAME       Directories which contain inline reference material
102 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
103 --ignore-files=FILES       Files or directories which should not be scanned
104                            May be used more than once for multiple directories
105 --output-dir=DIRNAME       Directory to put the generated DocBook files in
106 --tmpl-dir=DIRNAME         Directory in which template files may be found
107 --main-sgml-file=FILE      File containing the toplevel DocBook file.
108 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
109 --output-format=FORMAT     Format to use for the generated docbook, XML or SGML.
110 --{xml,sgml}-mode          Allow DocBook markup in inline documentation.
111 --default-stability=LEVEL  Specify default stability Level. Valid values are
112                            Stable, Unstable, or Private.
113 --default-includes=FILENAMES Specify default includes for section Synopsis
114 --name-space=NS            Omit namespace in index.
115 --version                  Print the version of this program
116 --help                     Print this help
118     exit 0;
121 my ($empty_element_end, $doctype_header);
123 # autodetect output format
124 if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
125     if (!$MAIN_SGML_FILE) {
126         if (-e "${MODULE}-docs.xml") {
127             $OUTPUT_FORMAT = "xml";
128         } else {
129             $OUTPUT_FORMAT = "sgml";
130         }
131     } else {
132         if ($MAIN_SGML_FILE =~ m/.*\.(.*ml)$/i) {
133             $OUTPUT_FORMAT = lc($1);
134         }
135     }
137 } else {
138     $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
141 #print "DEBUG: output-format: [$OUTPUT_FORMAT]\n";
143 if ($OUTPUT_FORMAT eq "xml") {
144     if (!$MAIN_SGML_FILE) {
145         # backwards compatibility
146         if (-e "${MODULE}-docs.sgml") {
147             $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
148         } else {
149             $MAIN_SGML_FILE = "${MODULE}-docs.xml";
150         }
151     }
152     $empty_element_end = "/>";
154     if (-e $MAIN_SGML_FILE) {
155         open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
156         $doctype_header = "";
157         while (<INPUT>) {
158             if (/^\s*<(book|chapter|article)/) {
159                 # check that the top-level tag or the doctype decl contain the xinclude namespace decl
160                 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
161                     $doctype_header = "";
162                 }
163                 last;
164             }
165             $doctype_header .= $_;
166         }
167         close(INPUT);
168         $doctype_header =~ s/<!DOCTYPE \w+/<!DOCTYPE refentry/;
169         # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
170         # FIXME: not sure if we can do this now, as people already work-around the problem
171         # $doctype_header =~ s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#g;
172     } else {
173         $doctype_header =
174 "<?xml version=\"1.0\"?>\n" .
175 "<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.3//EN\"\n" .
176 "               \"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd\"\n" .
177 "[\n" .
178 "  <!ENTITY % local.common.attrib \"xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'\">\n" .
179 "]>\n";
180     }
181 } else {
182     if (!$MAIN_SGML_FILE) {
183         $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
184     }
185     $empty_element_end = ">";
186     $doctype_header = "";
189 my $ROOT_DIR = ".";
191 # All the files are written in subdirectories beneath here.
192 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
194 # This is where we put all the DocBook output.
195 $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/$OUTPUT_FORMAT";
197 # This file contains the object hierarchy.
198 my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
200 # This file contains the interfaces.
201 my $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
203 # This file contains the prerequisites.
204 my $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
206 # This file contains signal arguments and names.
207 my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
209 # The file containing Arg information.
210 my $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
212 # These global arrays store information on signals. Each signal has an entry
213 # in each of these arrays at the same index, like a multi-dimensional array.
214 my @SignalObjects;        # The GtkObject which emits the signal.
215 my @SignalNames;        # The signal name.
216 my @SignalReturns;        # The return type.
217 my @SignalFlags;        # Flags for the signal
218 my @SignalPrototypes;        # The rest of the prototype of the signal handler.
220 # These global arrays store information on Args. Each Arg has an entry
221 # in each of these arrays at the same index, like a multi-dimensional array.
222 my @ArgObjects;                # The GtkObject which has the Arg.
223 my @ArgNames;                # The Arg name.
224 my @ArgTypes;                # The Arg type - gint, GtkArrowType etc.
225 my @ArgFlags;                # How the Arg can be used - readable/writable etc.
226 my @ArgNicks;                # The nickname of the Arg.
227 my @ArgBlurbs;          # Docstring of the Arg.
228 my @ArgDefaults;        # Default value of the Arg.
229 my @ArgRanges;                # The range of the Arg type
230 # These global hashes store declaration info keyed on a symbol name.
231 my %Declarations;
232 my %DeclarationTypes;
233 my %DeclarationConditional;
234 my %DeclarationOutput;
235 my %Deprecated;
236 my %Since;
237 my %StabilityLevel;
238 my %StructHasTypedef;
240 # These global hashes store the existing documentation.
241 my %SymbolDocs;
242 my %SymbolTypes;
243 my %SymbolParams;
244 my %SymbolSourceFile;
245 my %SymbolSourceLine;
247 # These global hashes store documentation scanned from the source files.
248 my %SourceSymbolDocs;
249 my %SourceSymbolParams;
250 my %SourceSymbolSourceFile;
251 my %SourceSymbolSourceLine;
253 # all documentation goes in here, so we can do coverage analysis
254 my %AllSymbols;
255 my %AllIncompleteSymbols;
256 my %AllUnusedSymbols;
257 my %AllDocumentedSymbols;
259 # Undeclared yet documented symbols
260 my %UndeclaredSymbols;
262 # These global arrays store GObject, subclasses and the hierarchy (also of
263 # non-object derived types).
264 my @Objects;
265 my @ObjectLevels;
266 my %ObjectRoots;
268 my %Interfaces;
269 my %Prerequisites;
271 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
272 # section they are defined
273 my %KnownSymbols;
274 my %SymbolSection;
275 my %SymbolSectionId;
277 # collects index entries
278 my %IndexEntriesFull;
279 my %IndexEntriesSince;
280 my %IndexEntriesDeprecated;
282 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
283 my %PreProcessorDirectives;
284 $PreProcessorDirectives{"assert"} = 1;
285 $PreProcessorDirectives{"define"} = 1;
286 $PreProcessorDirectives{"elif"} = 1;
287 $PreProcessorDirectives{"else"} = 1;
288 $PreProcessorDirectives{"endif"} = 1;
289 $PreProcessorDirectives{"error"} = 1;
290 $PreProcessorDirectives{"if"} = 1;
291 $PreProcessorDirectives{"ifdef"} = 1;
292 $PreProcessorDirectives{"ifndef"} = 1;
293 $PreProcessorDirectives{"include"} = 1;
294 $PreProcessorDirectives{"line"} = 1;
295 $PreProcessorDirectives{"pragma"} = 1;
296 $PreProcessorDirectives{"unassert"} = 1;
297 $PreProcessorDirectives{"undef"} = 1;
298 $PreProcessorDirectives{"warning"} = 1;
300 # remember used annotation (to write minimal glossary)
301 my %AnnotationsUsed;
303 # the annotations are defined at:
304 # https://live.gnome.org/GObjectIntrospection/Annotations
305 my %AnnotationDefinition = (
306     'allow-none' => "NULL is ok, both for passing and for returning.",
307     'array' => "Parameter points to an array of items.",
308     'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
309     'attributes' => "Free-form key-value pairs.",
310     'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
311     'constructor' => "This symbol is a constructor, not a static method.",
312     'destroy' => "This parameter is a 'destroy_data', for callbacks.",
313     'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
314     'element-type' => "Generics and defining elements of containers and arrays.",
315     'error-domains' => "Typed errors. Similar to throws in Java.",
316     'foreign' => "This is a foreign struct.",
317     'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
318     'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
319     'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
320     'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
321     'method' => "This is a method",
322     'not-error' => "A GError parameter is not to be handled like a normal GError.",
323     'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
324     'out caller-allocates' => "Out parameter, where caller must allocate storage.",
325     'out callee-allocates' => "Out parameter, where caller must allocate storage.",
326     'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
327     'rename-to' => "Rename the original symbol's name to SYMBOL.",
328     'scope call' => "The callback is valid only during the call to the method.",
329     'scope async' => "The callback is valid until first called.",
330     'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
331     'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
332     'skip' => "Exposed in C code, not necessarily available in other languages.",
333     'transfer container' => "Free data container after the code is done.",
334     'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
335     'transfer full' => "Free data after the code is done.",
336     'transfer none' => "Don't free data after the code is done.",
337     'type' => "Override the parsed C type with given type.",
338     'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
339     'virtual' => "This is the invoker for a virtual method.",
340     'value' => "The specified value overrides the evaluated value of the constant."
343 # Elements to consider non-block items in MarkDown parsing
344 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
345                                "emphasis" => 1,
346                                "envar" => 1,
347                                "filename" => 1,
348                                "firstterm" => 1,
349                                "function" => 1,
350                                "manvolnum" => 1,
351                                "option" => 1,
352                                "replaceable" => 1,
353                                "structname" => 1,
354                                "title" => 1,
355                                "varname" => 1 );
356 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
357                            "`" => 1,
358                            "*" => 1,
359                            "_" => 1,
360                            "{" => 1,
361                            "}" => 1,
362                            "[" => 1,
363                            "]" => 1,
364                            "(" => 1,
365                            ")" => 1,
366                            ">" => 1,
367                            "#" => 1,
368                            "+" => 1,
369                            "-" => 1,
370                            "." => 1,
371                            "!" => 1 );
372 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
373                                "%" => 1 );
375 # Create the root DocBook output directory if it doens't exist.
376 if (! -e $SGML_OUTPUT_DIR) {
377     mkdir ("$SGML_OUTPUT_DIR", 0777)
378         || die "Can't create directory: $SGML_OUTPUT_DIR";
381 # Function and other declaration output settings.
382 my $RETURN_TYPE_FIELD_WIDTH = 20;
383 my $SYMBOL_FIELD_WIDTH = 36;
384 my $MAX_SYMBOL_FIELD_WIDTH = 40;
385 my $SIGNAL_FIELD_WIDTH = 16;
386 my $PARAM_FIELD_COUNT = 2;
388 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
389 &ReadSignalsFile ($SIGNALS_FILE);
390 &ReadArgsFile ($ARGS_FILE);
391 &ReadObjectHierarchy;
392 &ReadInterfaces;
393 &ReadPrerequisites;
395 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
396 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
397     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
400 for my $dir (@SOURCE_DIRS) {
401     &ReadSourceDocumentation ($dir);
404 my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt");
406 # If any of the DocBook SGML files have changed, update the timestamp file (so
407 # it can be used for Makefile dependencies).
408 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
410     # try to detect the common prefix
411     # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
412     if ($NAME_SPACE eq "") {
413         $NAME_SPACE="";
414         my $pos=0;
415         my $ratio=0.0;
416         do {
417             my %prefix;
418             my $letter="";
419             foreach my $symbol (keys(%IndexEntriesFull)) {
420                 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
421                     if (length($symbol)>$pos) {
422                         $letter=substr($symbol,$pos,1);
423                         # stop prefix scanning
424                         if ($letter eq "_") {
425                             # stop on "_"
426                             last;
427                         }
428                         # Should we also stop on a uppercase char, if last was lowercase
429                         #   GtkWidget, if we have the 'W' and had the 't' before
430                         # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
431                         #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
432                         # need to recound each time as this is per symbol
433                         $prefix{uc($letter)}++;
434                     }
435                 }
436             }
437             if ($letter ne "" && $letter ne "_") {
438                 my $maxletter="";
439                 my $maxsymbols=0;
440                 foreach $letter (keys(%prefix)) {
441                     #print "$letter: $prefix{$letter}.\n";
442                     if ($prefix{$letter}>$maxsymbols) {
443                         $maxletter=$letter;
444                         $maxsymbols=$prefix{$letter};
445                     }
446                 }
447                 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
448                 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
449                 if ($ratio > 0.9) {
450                     # do another round
451                     $NAME_SPACE .= $maxletter;
452                 }
453                 $pos++;
454             }
455             else {
456                 $ratio=0.0;
457             }
458         } while ($ratio > 0.9);
459         #print "most symbols start with $NAME_SPACE\n";
460     }
462     &OutputIndexFull;
463     &OutputDeprecatedIndex;
464     &OutputSinceIndexes;
465     &OutputAnnotationGlossary;
467     open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
468         || die "Can't create $ROOT_DIR/sgml.stamp: $!";
469     print (TIMESTAMP "timestamp");
470     close (TIMESTAMP);
473 #############################################################################
474 # Function    : OutputObjectList
475 # Description : This outputs the alphabetical list of objects, in a columned
476 #                table.
477 #               FIXME: Currently this also outputs ancestor objects
478 #                which may not actually be in this module.
479 # Arguments   : none
480 #############################################################################
482 sub OutputObjectList {
483     my $cols = 3;
485     # FIXME: use $OUTPUT_FORMAT
486     # my $old_object_index = "$SGML_OUTPUT_DIR/object_index.$OUTPUT_FORMAT";
487     my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml";
488     my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new";
490     open (OUTPUT, ">$new_object_index")
491         || die "Can't create $new_object_index: $!";
493     if ($OUTPUT_FORMAT eq "xml") {
494         my $header = $doctype_header;
496         $header =~ s/<!DOCTYPE \w+/<!DOCTYPE informaltable/;
497         print (OUTPUT "$header");
498     }
500     print (OUTPUT <<EOF);
501 <informaltable pgwide="1" frame="none">
502 <tgroup cols="$cols">
503 <colspec colwidth="1*"${empty_element_end}
504 <colspec colwidth="1*"${empty_element_end}
505 <colspec colwidth="1*"${empty_element_end}
506 <tbody>
509     my $count = 0;
510     my $object;
511     foreach $object (sort (@Objects)) {
512         my $xref = &MakeXRef ($object);
513         if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
514         print (OUTPUT "<entry>$xref</entry>\n");
515         if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
516         $count++;
517     }
518     if ($count == 0) {
519         # emit an empty row, since empty tables are invalid
520         print (OUTPUT "<row><entry> </entry></row>\n");
521     }
522     else {
523         if ($count % $cols > 0) {
524             print (OUTPUT "</row>\n");
525         }
526     }
528     print (OUTPUT <<EOF);
529 </tbody></tgroup></informaltable>
531     close (OUTPUT);
533     &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
536 #############################################################################
537 # Function    : TrimTextBlock
538 # Description : Trims extra whitespace. Empty lines inside a block are
539 #                preserved.
540 # Arguments   : $desc - the text block to trim. May contain newlines.
541 #############################################################################
543 sub TrimTextBlock {
544   my ($desc) = @_;
545   
546   # strip leading spaces on the block
547   $desc =~ s/^\s+//s;
548   # strip trailing spaces on every line
549   $desc =~ s/\s+$/\n/mg;
550   
551   return $desc;
555 #############################################################################
556 # Function    : OutputSGML
557 # Description : This collects the output for each section of the docs, and
558 #                outputs each file when the end of the section is found.
559 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
560 #                the functions/macros/structs etc. being documented, organised
561 #                into sections and subsections.
562 #############################################################################
564 sub OutputSGML {
565     my ($file) = @_;
567     #print "Reading: $file\n";
568     open (INPUT, $file)
569         || die "Can't open $file: $!";
570     my $filename = "";
571     my $book_top = "";
572     my $book_bottom = "";
573     my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
574     my $section_includes = "";
575     my $in_section = 0;
576     my $title = "";
577     my $section_id = "";
578     my $subsection = "";
579     my $num_symbols;
580     my $changed = 0;
581     my $functions_synop = "";
582     my $other_synop = "";
583     my $functions_details = "";
584     my $other_details = "";
585     my $signals_synop = "";
586     my $signals_desc = "";
587     my $args_synop = "";
588     my $child_args_synop = "";
589     my $style_args_synop = "";
590     my $args_desc = "";
591     my $child_args_desc = "";
592     my $style_args_desc = "";
593     my $hierarchy = "";
594     my $interfaces = "";
595     my $implementations = "";
596     my $prerequisites = "";
597     my $derived = "";
598     my @file_objects = ();
599     my %templates = ();
600     my %symbol_def_line = ();
602     # merge the source docs, in case there are no templates
603     &MergeSourceDocumentation;
605     while (<INPUT>) {
606         if (m/^#/) {
607             next;
609         } elsif (m/^<SECTION>/) {
610             $num_symbols = 0;
611             $in_section = 1;
612             @file_objects = ();
613             %symbol_def_line = ();
615         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
616             $other_synop .= "\n";
617             $functions_synop .= "\n";
618             $subsection = $1;
620         } elsif (m/^<SUBSECTION>/) {
622         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
623             $title = $1;
624             #print "Section: $title\n";
626             # We don't want warnings if object & class structs aren't used.
627             $DeclarationOutput{$title} = 1;
628             $DeclarationOutput{"${title}Class"} = 1;
629             $DeclarationOutput{"${title}Iface"} = 1;
630             $DeclarationOutput{"${title}Interface"} = 1;
632         } elsif (m/^<FILE>(.*)<\/FILE>/) {
633             $filename = $1;
634             if (! defined $templates{$filename}) {
635                if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
636                    &MergeSourceDocumentation;
637                    $templates{$filename}=$.;
638                }
639             } else {
640                 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
641                     "Previous occurrence on line ".$templates{$filename}.".");
642             }
643             if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
644                 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
645                  # Remove trailing blanks
646                 $title =~ s/\s+$//;
647            }
649         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
650             if ($in_section) {
651                 $section_includes = $1;
652             } else {
653                 if (defined $DEFAULT_INCLUDES) {
654                     &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
655                 }
656                 else {
657                     $includes = $1;
658                 }
659             }
661         } elsif (m/^<\/SECTION>/) {
662             #print "End of section: $title\n";
663             if ($num_symbols > 0) {
664                 # collect documents
665                 if ($OUTPUT_FORMAT eq "xml") {
666                     $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
667                 } else {
668                     $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n";
669                     $book_bottom .= "    &$section_id;\n";
670                 }
672                 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
673                     if ($section_includes) {
674                         &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
675                     }
676                     $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
677                 }
678                 if ($section_includes eq "") {
679                     $section_includes = $includes;
680                 }
682                  $signals_synop =~ s/^\n*//g;
683                  $signals_synop =~ s/\n+$/\n/g;
684                 if ($signals_synop ne '') {
685                     $signals_synop = <<EOF;
686 <refsect1 id="$section_id.signals" role="signal_proto">
687 <title role="signal_proto.title">Signals</title>
688 <informaltable frame="none">
689 <tgroup cols="3">
690 <colspec colname="signals_return" colwidth="150px"/>
691 <colspec colname="signals_name" colwidth="300px"/>
692 <colspec colname="signals_flags" colwidth="200px"/>
693 <tbody>
694 ${signals_synop}
695 </tbody>
696 </tgroup>
697 </informaltable>
698 </refsect1>
700                      $signals_desc = TrimTextBlock($signals_desc);
701                     $signals_desc  = <<EOF;
702 <refsect1 id="$section_id.signal-details" role="signals">
703 <title role="signals.title">Signal Details</title>
704 $signals_desc
705 </refsect1>
707                 }
709                 $args_synop =~ s/^\n*//g;
710                 $args_synop =~ s/\n+$/\n/g;
711                 if ($args_synop ne '') {
712                     $args_synop = <<EOF;
713 <refsect1 id="$section_id.properties" role="properties">
714 <title role="properties.title">Properties</title>
715 <informaltable frame="none">
716 <tgroup cols="3">
717 <colspec colname="properties_type" colwidth="150px"/>
718 <colspec colname="properties_name" colwidth="300px"/>
719 <colspec colname="properties_flags" colwidth="200px"/>
720 <tbody>
721 ${args_synop}
722 </tbody>
723 </tgroup>
724 </informaltable>
725 </refsect1>
727                      $args_desc = TrimTextBlock($args_desc);
728                     $args_desc  = <<EOF;
729 <refsect1 id="$section_id.property-details" role="property_details">
730 <title role="property_details.title">Property Details</title>
731 $args_desc
732 </refsect1>
734                 }
736                 $child_args_synop =~ s/^\n*//g;
737                 $child_args_synop =~ s/\n+$/\n/g;
738                 if ($child_args_synop ne '') {
739                     $args_synop .= <<EOF;
740 <refsect1 id="$section_id.child-properties" role="child_properties">
741 <title role="child_properties.title">Child Properties</title>
742 <informaltable frame="none">
743 <tgroup cols="3">
744 <colspec colname="child_properties_type" colwidth="150px"/>
745 <colspec colname="child_properties_name" colwidth="300px"/>
746 <colspec colname="child_properties_flags" colwidth="200px"/>
747 <tbody>
748 ${child_args_synop}
749 </tbody>
750 </tgroup>
751 </informaltable>
752 </refsect1>
754                      $child_args_desc = TrimTextBlock($child_args_desc);
755                      $args_desc .= <<EOF;
756 <refsect1 id="$section_id.child-property-details" role="child_property_details">
757 <title role="child_property_details.title">Child Property Details</title>
758 $child_args_desc
759 </refsect1>
761                 }
763                 $style_args_synop =~ s/^\n*//g;
764                 $style_args_synop =~ s/\n+$/\n/g;
765                 if ($style_args_synop ne '') {
766                     $args_synop .= <<EOF;
767 <refsect1 id="$section_id.style-properties" role="style_properties">
768 <title role="style_properties.title">Style Properties</title>
769 <informaltable frame="none">
770 <tgroup cols="3">
771 <colspec colname="style_properties_type" colwidth="150px"/>
772 <colspec colname="style_properties_name" colwidth="300px"/>
773 <colspec colname="style_properties_flags" colwidth="200px"/>
774 <tbody>
775 ${style_args_synop}
776 </tbody>
777 </tgroup>
778 </informaltable>
779 </refsect1>
781                      $style_args_desc = TrimTextBlock($style_args_desc);
782                     $args_desc .= <<EOF;
783 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
784 <title role="style_properties_details.title">Style Property Details</title>
785 $style_args_desc
786 </refsect1>
788                 }
790                  $hierarchy = TrimTextBlock($hierarchy);
791                 if ($hierarchy ne "") {
792                     $hierarchy = <<EOF;
793 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
794 <title role="object_hierarchy.title">Object Hierarchy</title>
795 $hierarchy
796 </refsect1>
798                 }
800                  $interfaces =~ TrimTextBlock($interfaces);
801                 if ($interfaces ne "") {
802                     $interfaces = <<EOF;
803 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
804 <title role="impl_interfaces.title">Implemented Interfaces</title>
805 $interfaces
806 </refsect1>
808                 }
810                  $implementations = TrimTextBlock($implementations);
811                 if ($implementations ne "") {
812                     $implementations = <<EOF;
813 <refsect1 id="$section_id.implementations" role="implementations">
814 <title role="implementations.title">Known Implementations</title>
815 $implementations
816 </refsect1>
818                 }
820                  $prerequisites = TrimTextBlock($prerequisites);
821                 if ($prerequisites ne "") {
822                     $prerequisites = <<EOF;
823 <refsect1 id="$section_id.prerequisites" role="prerequisites">
824 <title role="prerequisites.title">Prerequisites</title>
825 $prerequisites
826 </refsect1>
828                 }
830                  $derived = TrimTextBlock($derived);
831                 if ($derived ne "") {
832                     $derived = <<EOF;
833 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
834 <title role="derived_interfaces.title">Known Derived Interfaces</title>
835 $derived
836 </refsect1>
838                 }
840                 $functions_synop =~ s/^\n*//g;
841                 $functions_synop =~ s/\n+$/\n/g;
842                 if ($functions_synop ne '') {
843                   $functions_synop = <<EOF;
844 <refsect1 id="$section_id.functions" role="functions_proto">
845 <title role="functions_proto.title">Functions</title>
846 <informaltable pgwide="1" frame="none">
847 <tgroup cols="2">
848 <colspec colname="functions_return" colwidth="150px"/>
849 <colspec colname="functions_name"/>
850 <tbody>
851 ${functions_synop}
852 </tbody>
853 </tgroup>
854 </informaltable>
855 </refsect1>
857                 }
859                 $other_synop =~ s/^\n*//g;
860                 $other_synop =~ s/\n+$/\n/g;
861                 if ($other_synop ne '') {
862                   $other_synop = <<EOF;
863 <refsect1 id="$section_id.other" role="other_proto">
864 <title role="other_proto.title">Types and Values</title>
865 <informaltable role="enum_members_table" pgwide="1" frame="none">
866 <tgroup cols="2">
867 <colspec colname="name" colwidth="150px"/>
868 <colspec colname="description"/>
869 <tbody>
870 ${other_synop}
871 </tbody>
872 </tgroup>
873 </informaltable>
874 </refsect1>
876                 }
878                 my $file_changed = &OutputSGMLFile ($filename, $title, $section_id,
879                                                     $section_includes,
880                                                     \$functions_synop, \$other_synop,
881                                                     \$functions_details, \$other_details,
882                                                     \$signals_synop, \$signals_desc,
883                                                     \$args_synop, \$args_desc,
884                                                     \$hierarchy, \$interfaces,
885                                                     \$implementations,
886                                                     \$prerequisites, \$derived,
887                                                     \@file_objects);
888                 if ($file_changed) {
889                     $changed = 1;
890                 }
891             }
892             $title = "";
893             $section_id = "";
894             $subsection = "";
895             $in_section = 0;
896             $section_includes = "";
897             $functions_synop = "";
898             $other_synop = "";
899             $functions_details = "";
900             $other_details = "";
901             $signals_synop = "";
902             $signals_desc = "";
903             $args_synop = "";
904             $child_args_synop = "";
905             $style_args_synop = "";
906             $args_desc = "";
907             $child_args_desc = "";
908             $style_args_desc = "";
909             $hierarchy = "";
910             $interfaces = "";
911             $implementations = "";
912             $prerequisites = "";
913             $derived = "";
915         } elsif (m/^(\S+)/) {
916             my $symbol = $1;
917             #print "  Symbol: $symbol\n";
919             # check for duplicate entries
920             if (! defined $symbol_def_line{$symbol}) {
921                 my $declaration = $Declarations{$symbol};
922                 if (defined ($declaration)) {
923                     # We don't want standard macros/functions of GObjects,
924                     # or private declarations.
925                     if ($subsection ne "Standard" && $subsection ne "Private") {
926                         if (&CheckIsObject ($symbol)) {
927                             push @file_objects, $symbol;
928                         }
929                         my ($synop, $desc) = &OutputDeclaration ($symbol,
930                                                                  $declaration);
931                         my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
932                         my ($arg_synop, $child_arg_synop, $style_arg_synop,
933                             $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
934                         my $hier = &GetHierarchy ($symbol);
935                         my $ifaces = &GetInterfaces ($symbol);
936                         my $impls = &GetImplementations ($symbol);
937                         my $prereqs = &GetPrerequisites ($symbol);
938                         my $der = &GetDerived ($symbol);
939                         my $type = $DeclarationTypes {$symbol};
941                         if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
942                           $functions_synop .= $synop;
943                           $functions_details .= $desc;
944                         } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
945                           $functions_synop .= $synop;
946                           $functions_details .= $desc;
947                         } else {
948                           $other_synop .= $synop;
949                           $other_details .= $desc;
950                         }
951                         $signals_synop .= $sig_synop;
952                         $signals_desc .= $sig_desc;
953                         $args_synop .= $arg_synop;
954                         $child_args_synop .= $child_arg_synop;
955                         $style_args_synop .= $style_arg_synop;
956                         $args_desc .= $arg_desc;
957                         $child_args_desc .= $child_arg_desc;
958                         $style_args_desc .= $style_arg_desc;
959                         $hierarchy .= $hier;
960                         $interfaces .= $ifaces;
961                         $implementations .= $impls;
962                         $prerequisites .= $prereqs;
963                         $derived .= $der;
964                     }
966                     # Note that the declaration has been output.
967                     $DeclarationOutput{$symbol} = 1;
968                 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
969                     $UndeclaredSymbols{$symbol} = 1;
970                     &LogWarning ($file, $., "No declaration found for $symbol.");
971                 }
972                 $num_symbols++;
973                 $symbol_def_line{$symbol}=$.;
975                 if ($section_id eq "") {
976                     if($title eq "" && $filename eq "") {
977                         &LogWarning ($file, $., "Section has no title and no file.");
978                     }
979                     # FIXME: one of those would be enough
980                     # filename should be an internal detail for gtk-doc
981                     if ($title eq "") {
982                         $title = $filename;
983                     } elsif ($filename eq "") {
984                         $filename = $title;
985                     }
986                     $filename =~ s/\s/_/g;
988                     $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
989                     if (defined ($section_id) && $section_id !~ m/^\s*$/) {
990                         # Remove trailing blanks and use as is
991                         $section_id =~ s/\s+$//;
992                     } elsif (&CheckIsObject ($title)) {
993                         # GObjects use their class name as the ID.
994                         $section_id = &CreateValidSGMLID ($title);
995                     } else {
996                         $section_id = &CreateValidSGMLID ("$MODULE-$title");
997                     }
998                 }
999                 $SymbolSection{$symbol}=$title;
1000                 $SymbolSectionId{$symbol}=$section_id;
1001             }
1002             else {
1003                 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1004                     "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1005             }
1006         }
1007     }
1008     close (INPUT);
1010     &OutputMissingDocumentation;
1011     &OutputUndeclaredSymbols;
1012     &OutputUnusedSymbols;
1014     if ($OUTPUT_ALL_SYMBOLS) {
1015         &OutputAllSymbols;
1016     }
1017     if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1018         &OutputSymbolsWithoutSince;
1019     }
1021     for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1022         my $file_changed = &OutputExtraFile ($filename);
1023         if ($file_changed) {
1024             $changed = 1;
1025         }
1026     }
1028     &OutputBook ($book_top, $book_bottom);
1030     return $changed;
1033 #############################################################################
1034 # Function    : OutputIndex
1035 # Description : This writes an indexlist that can be included into the main-
1036 #               document into an <index> tag.
1037 #############################################################################
1039 sub OutputIndex {
1040     my ($basename, $apiindexref ) = @_;
1041     my %apiindex = %{$apiindexref};
1042     my $old_index = "$SGML_OUTPUT_DIR/$basename.xml";
1043     my $new_index = "$SGML_OUTPUT_DIR/$basename.new";
1044     my $lastletter = " ";
1045     my $divopen = 0;
1046     my $symbol;
1047     my $short_symbol;
1049     open (OUTPUT, ">$new_index")
1050         || die "Can't create $new_index";
1052     my $header = $doctype_header;
1053     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE indexdiv/;
1055     print (OUTPUT "$header<indexdiv>\n");
1057     #print "generate $basename index (".%apiindex." entries)\n";
1059     # do a case insensitive sort while chopping off the prefix
1060     foreach my $hash (
1061         sort { $$a{criteria} cmp $$b{criteria} }
1062         map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1063         keys %apiindex) {
1065         $symbol = $$hash{original};
1066         if (defined($$hash{short})) {
1067             $short_symbol = $$hash{short};
1068         } else {
1069             $short_symbol = $symbol;
1070         }
1072         # generate a short symbol description
1073         my $symbol_desc = "";
1074         my $symbol_section = "";
1075         my $symbol_section_id = "";
1076         my $symbol_type = "";
1077         if (defined($DeclarationTypes{$symbol})) {
1078           $symbol_type = lc($DeclarationTypes{$symbol});
1079         }
1080         if ($symbol_type eq "") {
1081             #print "trying symbol $symbol\n";
1082             if ($symbol =~ m/(.*)::(.*)/) {
1083                 my $oname = $1;
1084                 my $osym = $2;
1085                 my $i;
1086                 #print "  trying object signal ${oname}:$osym in ".$#SignalNames." signals\n";
1087                 for ($i = 0; $i <= $#SignalNames; $i++) {
1088                     if ($SignalNames[$i] eq $osym) {
1089                         $symbol_type = "object signal";
1090                         if (defined($SymbolSection{$oname})) {
1091                            $symbol_section = $SymbolSection{$oname};
1092                            $symbol_section_id = $SymbolSectionId{$oname};
1093                         }
1094                         last;
1095                     }
1096                 }
1097             } elsif ($symbol =~ m/(.*):(.*)/) {
1098                 my $oname = $1;
1099                 my $osym = $2;
1100                 my $i;
1101                 #print "  trying object property ${oname}::$osym in ".$#ArgNames." properties\n";
1102                 for ($i = 0; $i <= $#ArgNames; $i++) {
1103                     #print "    ".$ArgNames[$i]."\n";
1104                     if ($ArgNames[$i] eq $osym) {
1105                         $symbol_type = "object property";
1106                         if (defined($SymbolSection{$oname})) {
1107                            $symbol_section = $SymbolSection{$oname};
1108                            $symbol_section_id = $SymbolSectionId{$oname};
1109                         }
1110                         last;
1111                     }
1112                 }
1113             }
1114         } else {
1115            if (defined($SymbolSection{$symbol})) {
1116                $symbol_section = $SymbolSection{$symbol};
1117                $symbol_section_id = $SymbolSectionId{$symbol};
1118            }
1119         }
1120         if ($symbol_type ne "") {
1121            $symbol_desc=", $symbol_type";
1122            if ($symbol_section ne "") {
1123                $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1124                #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1125            }
1126         }
1128         my $curletter = uc(substr($short_symbol,0,1));
1129         my $id = $apiindex{$symbol};
1131         #print "  add symbol $symbol with $id to index in section $curletter\n";
1133         if ($curletter ne $lastletter) {
1134             $lastletter = $curletter;
1136             if ($divopen == 1) {
1137                 print (OUTPUT "</indexdiv>\n");
1138             }
1139             print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1140             $divopen = 1;
1141         }
1143         print (OUTPUT <<EOF);
1144 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1146     }
1148     if ($divopen == 1) {
1149         print (OUTPUT "</indexdiv>\n");
1150     }
1151     print (OUTPUT "</indexdiv>\n");
1152     close (OUTPUT);
1154     &UpdateFileIfChanged ($old_index, $new_index, 0);
1158 #############################################################################
1159 # Function    : OutputIndexFull
1160 # Description : This writes the full api indexlist that can be included into the
1161 #               main document into an <index> tag.
1162 #############################################################################
1164 sub OutputIndexFull {
1165     &OutputIndex ("api-index-full", \%IndexEntriesFull);
1169 #############################################################################
1170 # Function    : OutputDeprecatedIndex
1171 # Description : This writes the deprecated api indexlist that can be included
1172 #               into the main document into an <index> tag.
1173 #############################################################################
1175 sub OutputDeprecatedIndex {
1176     &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1180 #############################################################################
1181 # Function    : OutputSinceIndexes
1182 # Description : This writes the 'since' api indexlists that can be included into
1183 #               the main document into an <index> tag.
1184 #############################################################################
1186 sub OutputSinceIndexes {
1187     my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1189     foreach my $version (@sinces) {
1190         #print "Since : [$version]\n";
1191         # TODO make filtered hash
1192         #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1193         my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1195         &OutputIndex ("api-index-$version", \%index);
1196     }
1199 #############################################################################
1200 # Function    : OutputAnnotationGlossary
1201 # Description : This writes a glossary of the used annotation terms into a
1202 #               separate glossary file that can be included into the main
1203 #               document.
1204 #############################################################################
1206 sub OutputAnnotationGlossary {
1207     my $old_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.xml";
1208     my $new_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.new";
1209     my $lastletter = " ";
1210     my $divopen = 0;
1212     # if there are no annotations used return
1213     return if (! keys(%AnnotationsUsed));
1215     # add acronyms that are referenced from acronym text
1216 rerun:
1217     foreach my $annotation (keys(%AnnotationsUsed)) {
1218         if(defined($AnnotationDefinition{$annotation})) {
1219             if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1220                 if (!exists($AnnotationsUsed{$1})) {
1221                     $AnnotationsUsed{$1} = 1;
1222                     goto rerun;
1223                 }
1224             }
1225         }
1226     }
1228     open (OUTPUT, ">$new_glossary")
1229         || die "Can't create $new_glossary";
1231     my $header = $doctype_header;
1232     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE glossary/;
1234     print (OUTPUT  <<EOF);
1235 $header
1236 <glossary id="annotation-glossary">
1237   <title>Annotation Glossary</title>
1240     foreach my $annotation (sort(keys(%AnnotationsUsed))) {
1241         if(defined($AnnotationDefinition{$annotation})) {
1242             my $def = $AnnotationDefinition{$annotation};
1243             my $curletter = uc(substr($annotation,0,1));
1245             if ($curletter ne $lastletter) {
1246                 $lastletter = $curletter;
1248                 if ($divopen == 1) {
1249                     print (OUTPUT "</glossdiv>\n");
1250                 }
1251                 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1252                 $divopen = 1;
1253             }
1254             print (OUTPUT <<EOF);
1255     <glossentry>
1256       <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1257       <glossdef>
1258         <para>$def</para>
1259       </glossdef>
1260     </glossentry>
1262         }
1263     }
1265     if ($divopen == 1) {
1266         print (OUTPUT "</glossdiv>\n");
1267     }
1268     print (OUTPUT "</glossary>\n");
1269     close (OUTPUT);
1271     &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1274 #############################################################################
1275 # Function    : ReadKnownSymbols
1276 # Description : This collects the names of non-private symbols from the
1277 #               $MODULE-sections.txt file.
1278 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
1279 #                the functions/macros/structs etc. being documented, organised
1280 #                into sections and subsections.
1281 #############################################################################
1283 sub ReadKnownSymbols {
1284     my ($file) = @_;
1286     my $subsection = "";
1288     #print "Reading: $file\n";
1289     open (INPUT, $file)
1290         || die "Can't open $file: $!";
1292     while (<INPUT>) {
1293         if (m/^#/) {
1294             next;
1296         } elsif (m/^<SECTION>/) {
1297             $subsection = "";
1299         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1300             $subsection = $1;
1302         } elsif (m/^<SUBSECTION>/) {
1303             next;
1305         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1306             next;
1308         } elsif (m/^<FILE>(.*)<\/FILE>/) {
1309             $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1310             $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1311             next;
1313         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1314             next;
1316         } elsif (m/^<\/SECTION>/) {
1317             next;
1319         } elsif (m/^(\S+)/) {
1320             my $symbol = $1;
1322             if ($subsection ne "Standard" && $subsection ne "Private") {
1323                 $KnownSymbols{$symbol} = 1;
1324             }
1325             else {
1326                 $KnownSymbols{$symbol} = 0;
1327             }
1328         }
1329     }
1330     close (INPUT);
1334 #############################################################################
1335 # Function    : OutputDeclaration
1336 # Description : Returns the synopsis and detailed description DocBook
1337 #                describing one function/macro etc.
1338 # Arguments   : $symbol - the name of the function/macro begin described.
1339 #                $declaration - the declaration of the function/macro.
1340 #############################################################################
1342 sub OutputDeclaration {
1343     my ($symbol, $declaration) = @_;
1345     my $type = $DeclarationTypes {$symbol};
1346     if ($type eq 'MACRO') {
1347         return &OutputMacro ($symbol, $declaration);
1348     } elsif ($type eq 'TYPEDEF') {
1349         return &OutputTypedef ($symbol, $declaration);
1350     } elsif ($type eq 'STRUCT') {
1351         return &OutputStruct ($symbol, $declaration);
1352     } elsif ($type eq 'ENUM') {
1353         return &OutputEnum ($symbol, $declaration);
1354     } elsif ($type eq 'UNION') {
1355         return &OutputUnion ($symbol, $declaration);
1356     } elsif ($type eq 'VARIABLE') {
1357         return &OutputVariable ($symbol, $declaration);
1358     } elsif ($type eq 'FUNCTION') {
1359         return &OutputFunction ($symbol, $declaration, $type);
1360     } elsif ($type eq 'USER_FUNCTION') {
1361         return &OutputFunction ($symbol, $declaration, $type);
1362     } else {
1363         die "Unknown symbol type";
1364     }
1368 #############################################################################
1369 # Function    : OutputSymbolTraits
1370 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1371 # Arguments   : $symbol - the name of the function/macro begin described.
1372 #############################################################################
1374 sub OutputSymbolTraits {
1375     my ($symbol) = @_;
1376     my $desc = "";
1378     if (exists $Since{$symbol}) {
1379         $desc .= "<para role=\"since\">Since $Since{$symbol}</para>";
1380     }
1381     if (exists $StabilityLevel{$symbol}) {
1382         $desc .= "<para role=\"stability\">Stability Level: $StabilityLevel{$symbol}</para>";
1383     }
1384     return $desc;
1387 #############################################################################
1388 # Function    : Output{Symbol,Section}ExtraLinks
1389 # Description : Returns extralinks for the symbol (if enabled).
1390 # Arguments   : $symbol - the name of the function/macro begin described.
1391 #############################################################################
1393 sub uri_escape {
1394     my $text = $_[0];
1395     return undef unless defined $text;
1397     # Build a char to hex map
1398     my %escapes = ();
1399     for (0..255) {
1400             $escapes{chr($_)} = sprintf("%%%02X", $_);
1401     }
1403     # Default unsafe characters.  RFC 2732 ^(uric - reserved)
1404     $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1406     return $text;
1409 sub OutputSymbolExtraLinks {
1410     my ($symbol) = @_;
1411     my $desc = "";
1413     if (0) { # NEW FEATURE: needs configurability
1414     my $sstr = &uri_escape($symbol);
1415     my $mstr = &uri_escape($MODULE);
1416     $desc .= <<EOF;
1417 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1418 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$sstr">edit documentation</ulink>
1420     }
1421     return $desc;
1424 sub OutputSectionExtraLinks {
1425     my ($symbol,$docsymbol) = @_;
1426     my $desc = "";
1428     if (0) { # NEW FEATURE: needs configurability
1429     my $sstr = &uri_escape($symbol);
1430     my $mstr = &uri_escape($MODULE);
1431     my $dsstr = &uri_escape($docsymbol);
1432     $desc .= <<EOF;
1433 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1434 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$dsstr">edit documentation</ulink>
1436     }
1437     return $desc;
1441 #############################################################################
1442 # Function    : OutputMacro
1443 # Description : Returns the synopsis and detailed description of a macro.
1444 # Arguments   : $symbol - the macro.
1445 #                $declaration - the declaration of the macro.
1446 #############################################################################
1448 sub OutputMacro {
1449     my ($symbol, $declaration) = @_;
1450     my $id = &CreateValidSGMLID ($symbol);
1451     my $condition = &MakeConditionDescription ($symbol);
1452     my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1453     my $desc;
1455     my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1456     my $title = $symbol . (@fields ? "()" : "");
1458     $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1459     $desc .= MakeIndexterms($symbol, $id);
1460     $desc .= "\n";
1461     $desc .= OutputSymbolExtraLinks($symbol);
1463     if (@fields) {
1464         if (length ($symbol) < $SYMBOL_FIELD_WIDTH) {
1465             $synop .= (' ' x ($SYMBOL_FIELD_WIDTH - length ($symbol)));
1466         }
1468         $synop .= "(";
1469         for (my $i = 1; $i <= $#fields; $i += 2) {
1470             my $field_name = $fields[$i];
1472             if ($i == 1) {
1473                 $synop .= "$field_name";
1474             } else {
1475                 $synop .= ",\n"
1476                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1477                     . " $field_name";
1478             }
1479         }
1480         $synop .= ")";
1481     }
1482     $synop .= "</entry></row>\n";
1484     # Don't output the macro definition if is is a conditional macro or it
1485     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1486     # longer than 2 lines, otherwise we get lots of complicated macros like
1487     # g_assert.
1488     if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1489         && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1490         my $decl_out = &CreateValidSGML ($declaration);
1491         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1492     } else {
1493         $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1494         if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1495             my $args = $1;
1496             my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1497             # Align each line so that if should all line up OK.
1498             $args =~ s/\n/\n$pad/gm;
1499             $desc .= &CreateValidSGML ($args);
1500         }
1501         $desc .= "</programlisting>\n";
1502     }
1504     $desc .= &MakeDeprecationNote($symbol);
1506     my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1507     my $parameters_output = 0;
1509     if (defined ($SymbolDocs{$symbol})) {
1510         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1512         # Try to insert the parameter table at the author's desired position.
1513         # Otherwise we need to tag it onto the end.
1514         if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
1515           $parameters_output = 1;
1516         }
1517         $desc .= $symbol_docs;
1518     }
1520     if ($parameters_output == 0) {
1521         $desc .= $parameters;
1522     }
1524     $desc .= OutputSymbolTraits ($symbol);
1525     $desc .= "</refsect2>\n";
1526     return ($synop, $desc);
1530 #############################################################################
1531 # Function    : OutputTypedef
1532 # Description : Returns the synopsis and detailed description of a typedef.
1533 # Arguments   : $symbol - the typedef.
1534 #                $declaration - the declaration of the typedef,
1535 #                  e.g. 'typedef unsigned int guint;'
1536 #############################################################################
1538 sub OutputTypedef {
1539     my ($symbol, $declaration) = @_;
1540     my $id = &CreateValidSGMLID ($symbol);
1541     my $condition = &MakeConditionDescription ($symbol);
1542     my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1543     my $synop = "<row><entry role=\"c_punctuation\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1545     $desc .= MakeIndexterms($symbol, $id);
1546     $desc .= "\n";
1547     $desc .= OutputSymbolExtraLinks($symbol);
1549     if (!defined ($DeclarationConditional{$symbol})) {
1550         my $decl_out = &CreateValidSGML ($declaration);
1551         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1552     }
1554     $desc .= &MakeDeprecationNote($symbol);
1556     if (defined ($SymbolDocs{$symbol})) {
1557         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1558     }
1559     $desc .= OutputSymbolTraits ($symbol);
1560     $desc .= "</refsect2>\n";
1561     return ($synop, $desc);
1565 #############################################################################
1566 # Function    : OutputStruct
1567 # Description : Returns the synopsis and detailed description of a struct.
1568 #                We check if it is a object struct, and if so we only output
1569 #                parts of it that are noted as public fields.
1570 #                We also use a different SGML ID for object structs, since the
1571 #                original ID is used for the entire RefEntry.
1572 # Arguments   : $symbol - the struct.
1573 #                $declaration - the declaration of the struct.
1574 #############################################################################
1576 sub OutputStruct {
1577     my ($symbol, $declaration) = @_;
1579     my $is_gtype = 0;
1580     my $default_to_public = 1;
1581     if (&CheckIsObject ($symbol)) {
1582         #print "Found struct gtype: $symbol\n";
1583         $is_gtype = 1;
1584         $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1585     }
1587     my $id;
1588     my $condition;
1589     if ($is_gtype) {
1590         $id = &CreateValidSGMLID ($symbol . "_struct");
1591         $condition = &MakeConditionDescription ($symbol . "_struct");
1592     } else {
1593         $id = &CreateValidSGMLID ($symbol);
1594         $condition = &MakeConditionDescription ($symbol);
1595     }
1597     # Determine if it is a simple struct or it also has a typedef.
1598     my $has_typedef = 0;
1599     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1600       $has_typedef = 1;
1601     }
1603     my $type_output;
1604     my $desc;
1605     if ($has_typedef) {
1606         # For structs with typedefs we just output the struct name.
1607         $type_output = "";
1608         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1609     } else {
1610         $type_output = "struct";
1611         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1612     }
1613     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1615     $desc .= MakeIndexterms($symbol, $id);
1616     $desc .= "\n";
1617     $desc .= OutputSymbolExtraLinks($symbol);
1619     # Form a pretty-printed, private-data-removed form of the declaration
1621     my $decl_out = "";
1622     if ($declaration =~ m/^\s*$/) {
1623         #print "Found opaque struct: $symbol\n";
1624         $decl_out = "typedef struct _$symbol $symbol;";
1625     } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1626         #print "Found opaque struct: $symbol\n";
1627         $decl_out = "struct $symbol;";
1628     } else {
1629         my $public = $default_to_public;
1630         my $new_declaration = "";
1631         my $decl_line;
1632         my $decl = $declaration;
1634         if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1635             my $struct_contents = $2;
1637             foreach $decl_line (split (/\n/, $struct_contents)) {
1638                 #print "Struct line: $decl_line\n";
1639                 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1640                     $public = 1;
1641                 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1642                     $public = 0;
1643                 } elsif ($public) {
1644                     $new_declaration .= $decl_line . "\n";
1645                 }
1646             }
1648             if ($new_declaration) {
1649                 # Strip any blank lines off the ends.
1650                 $new_declaration =~ s/^\s*\n//;
1651                 $new_declaration =~ s/\n\s*$/\n/;
1653                 if ($has_typedef) {
1654                     $decl_out = "typedef struct {\n" . $new_declaration
1655                       . "} $symbol;\n";
1656                 } else {
1657                     $decl_out = "struct $symbol {\n" . $new_declaration
1658                       . "};\n";
1659                 }
1660             }
1661         } else {
1662             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1663                 "Couldn't parse struct:\n$declaration");
1664         }
1666         # If we couldn't parse the struct or it was all private, output an
1667         # empty struct declaration.
1668         if ($decl_out eq "") {
1669             if ($has_typedef) {
1670                 $decl_out = "typedef struct _$symbol $symbol;";
1671             } else {
1672                 $decl_out = "struct $symbol;";
1673             }
1674         }
1675     }
1677     $decl_out = &CreateValidSGML ($decl_out);
1678     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1680     $desc .= &MakeDeprecationNote($symbol);
1682     if (defined ($SymbolDocs{$symbol})) {
1683         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1684     }
1686     # Create a table of fields and descriptions
1688     # FIXME: Inserting &#160's into the produced type declarations here would
1689     #        improve the output in most situations ... except for function
1690     #        members of structs!
1691     my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1692                                         0, \&MakeXRef,
1693                                         sub {
1694                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1695                                         });
1696     my $params = $SymbolParams{$symbol};
1698     # If no parameters are filled in, we don't generate the description
1699     # table, for backwards compatibility.
1701     my $found = 0;
1702     if (defined $params) {
1703         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1704             if ($params->[$i] =~ /\S/) {
1705                 $found = 1;
1706                 last;
1707             }
1708         }
1709     }
1711     if ($found) {
1712         my %field_descrs = @$params;
1713         my $missing_parameters = "";
1714         my $unused_parameters = "";
1716         $desc .= <<EOF;
1717 <refsect3 role="struct_members">\n<title>Members</title>
1718 <informaltable role="struct_members_table" pgwide="1" frame="none">
1719 <tgroup cols="3">
1720 <colspec colname="struct_members_name" colwidth="300px"/>
1721 <colspec colname="struct_members_description"/>
1722 <colspec colname="struct_members_annotations" colwidth="200px"/>
1723 <tbody>
1726         while (@fields) {
1727             my $field_name = shift @fields;
1728             my $text = shift @fields;
1729             my $field_descr = $field_descrs{$field_name};
1730             my $param_annotations = "";
1732             $desc .= "<row><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1733             if (defined $field_descr) {
1734                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1735                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1736                 # trim
1737                 $field_descr =~ s/^(\s|\n)+//msg;
1738                 $field_descr =~ s/(\s|\n)+$//msg;
1739                 $desc .= "<listitem>$field_descr</listitem>\n";
1740                 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1741                 delete $field_descrs{$field_name};
1742             } else {
1743                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1744                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1745                 if ($missing_parameters ne "") {
1746                   $missing_parameters .= ", ".$field_name;
1747                 } else {
1748                     $missing_parameters = $field_name;
1749                 }
1750                 $desc .= "<entry /><entry />\n";
1751             }
1752             $desc .= "</row>\n";
1753         }
1754         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1755         foreach my $field_name (keys %field_descrs) {
1756             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1757                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1758             if ($unused_parameters ne "") {
1759               $unused_parameters .= ", ".$field_name;
1760             } else {
1761                $unused_parameters = $field_name;
1762             }
1763         }
1765         # remember missing/unused parameters (needed in tmpl-free build)
1766         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1767             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1768         }
1769         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1770             $AllUnusedSymbols{$symbol}=$unused_parameters;
1771         }
1772     }
1773     else {
1774         if (scalar(@fields) > 0) {
1775             if (! exists ($AllIncompleteSymbols{$symbol})) {
1776                 $AllIncompleteSymbols{$symbol}="<items>";
1777                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1778                     "Field descriptions for struct $symbol are missing in source code comment block.");
1779                 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1780             }
1781         }
1782     }
1784     $desc .= OutputSymbolTraits ($symbol);
1785     $desc .= "</refsect2>\n";
1786     return ($synop, $desc);
1790 #############################################################################
1791 # Function    : OutputUnion
1792 # Description : Returns the synopsis and detailed description of a union.
1793 # Arguments   : $symbol - the union.
1794 #                $declaration - the declaration of the union.
1795 #############################################################################
1797 sub OutputUnion {
1798     my ($symbol, $declaration) = @_;
1800     my $is_gtype = 0;
1801     if (&CheckIsObject ($symbol)) {
1802         @TRACE@("Found union gtype: $symbol\n");
1803         $is_gtype = 1;
1804     }
1806     my $id;
1807     my $condition;
1808     if ($is_gtype) {
1809         $id = &CreateValidSGMLID ($symbol . "_union");
1810         $condition = &MakeConditionDescription ($symbol . "_union");
1811     } else {
1812         $id = &CreateValidSGMLID ($symbol);
1813         $condition = &MakeConditionDescription ($symbol);
1814     }
1816     # Determine if it is a simple struct or it also has a typedef.
1817     my $has_typedef = 0;
1818     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1819       $has_typedef = 1;
1820     }
1822     my $type_output;
1823     my $desc;
1824     if ($has_typedef) {
1825         # For unions with typedefs we just output the union name.
1826         $type_output = "";
1827         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1828     } else {
1829         $type_output = "union";
1830         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1831     }
1832     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1834     $desc .= MakeIndexterms($symbol, $id);
1835     $desc .= "\n";
1836     $desc .= OutputSymbolExtraLinks($symbol);
1837     $desc .= &MakeDeprecationNote($symbol);
1839     if (defined ($SymbolDocs{$symbol})) {
1840         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1841     }
1843     # Create a table of fields and descriptions
1845     # FIXME: Inserting &#160's into the produced type declarations here would
1846     #        improve the output in most situations ... except for function
1847     #        members of structs!
1848     my @fields = ParseStructDeclaration($declaration, 0,
1849                                         0, \&MakeXRef,
1850                                         sub {
1851                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1852                                         });
1853     my $params = $SymbolParams{$symbol};
1855     # If no parameters are filled in, we don't generate the description
1856     # table, for backwards compatibility
1858     my $found = 0;
1859     if (defined $params) {
1860         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1861             if ($params->[$i] =~ /\S/) {
1862                 $found = 1;
1863                 last;
1864             }
1865         }
1866     }
1868     if ($found) {
1869         my %field_descrs = @$params;
1870         my $missing_parameters = "";
1871         my $unused_parameters = "";
1873         $desc .= <<EOF;
1874 <refsect3 role="union_members">\n<title>Members</title>
1875 <informaltable role="union_members_table" pgwide="1" frame="none">
1876 <tgroup cols="3">
1877 <colspec colname="union_members_name" colwidth="300px"/>
1878 <colspec colname="union_members_description"/>
1879 <colspec colname="union_members_annotations" colwidth="200px"/>
1880 <tbody>
1883         while (@fields) {
1884             my $field_name = shift @fields;
1885             my $text = shift @fields;
1886             my $field_descr = $field_descrs{$field_name};
1887             my $param_annotations = "";
1889             $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1890             if (defined $field_descr) {
1891                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1892                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1894                 # trim
1895                 $field_descr =~ s/^(\s|\n)+//msg;
1896                 $field_descr =~ s/(\s|\n)+$//msg;
1897                 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1898                 delete $field_descrs{$field_name};
1899             } else {
1900                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1901                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1902                 if ($missing_parameters ne "") {
1903                     $missing_parameters .= ", ".$field_name;
1904                 } else {
1905                     $missing_parameters = $field_name;
1906                 }
1907                 $desc .= "<entry /><entry />\n";
1908             }
1909             $desc .= "</row>\n";
1910         }
1911         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1912         foreach my $field_name (keys %field_descrs) {
1913             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1914                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1915             if ($unused_parameters ne "") {
1916               $unused_parameters .= ", ".$field_name;
1917             } else {
1918                $unused_parameters = $field_name;
1919             }
1920         }
1922         # remember missing/unused parameters (needed in tmpl-free build)
1923         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1924             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1925         }
1926         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1927             $AllUnusedSymbols{$symbol}=$unused_parameters;
1928         }
1929     }
1930     else {
1931         if (scalar(@fields) > 0) {
1932             if (! exists ($AllIncompleteSymbols{$symbol})) {
1933                 $AllIncompleteSymbols{$symbol}="<items>";
1934                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1935                     "Field descriptions for union $symbol are missing in source code comment block.");
1936                 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1937             }
1938         }
1939     }
1941     $desc .= OutputSymbolTraits ($symbol);
1942     $desc .= "</refsect2>\n";
1943     return ($synop, $desc);
1947 #############################################################################
1948 # Function    : OutputEnum
1949 # Description : Returns the synopsis and detailed description of a enum.
1950 # Arguments   : $symbol - the enum.
1951 #                $declaration - the declaration of the enum.
1952 #############################################################################
1954 sub OutputEnum {
1955     my ($symbol, $declaration) = @_;
1957     my $is_gtype = 0;
1958     if (&CheckIsObject ($symbol)) {
1959         #print "Found enum gtype: $symbol\n";
1960         $is_gtype = 1;
1961     }
1963     my $id;
1964     my $condition;
1965     if ($is_gtype) {
1966         $id = &CreateValidSGMLID ($symbol . "_enum");
1967         $condition = &MakeConditionDescription ($symbol . "_enum");
1968     } else {
1969         $id = &CreateValidSGMLID ($symbol);
1970         $condition = &MakeConditionDescription ($symbol);
1971     }
1973     my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1974     my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1976     $desc .= MakeIndexterms($symbol, $id);
1977     $desc .= "\n";
1978     $desc .= OutputSymbolExtraLinks($symbol);
1979     $desc .= &MakeDeprecationNote($symbol);
1981     if (defined ($SymbolDocs{$symbol})) {
1982         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1983     }
1985     # Create a table of fields and descriptions
1987     my @fields = ParseEnumDeclaration($declaration);
1988     my $params = $SymbolParams{$symbol};
1990     # If nothing at all is documented log a single summary warning at the end.
1991     # Otherwise, warn about each undocumented item.
1993     my $found = 0;
1994     if (defined $params) {
1995         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1996             if ($params->[$i] =~ /\S/) {
1997                 $found = 1;
1998                 last;
1999             }
2000         }
2001     }
2003     my %field_descrs = (defined $params ? @$params : ());
2004     my $missing_parameters = "";
2005     my $unused_parameters = "";
2007     $desc .= <<EOF;
2008 <refsect3 role="enum_members">\n<title>Members</title>
2009 <informaltable role="enum_members_table" pgwide="1" frame="none">
2010 <tgroup cols="3">
2011 <colspec colname="enum_members_name" colwidth="300px"/>
2012 <colspec colname="enum_members_description"/>
2013 <colspec colname="enum_members_annotations" colwidth="200px"/>
2014 <tbody>
2017     for my $field_name (@fields) {
2018         my $field_descr = $field_descrs{$field_name};
2019         my $param_annotations = "";
2021         $id = &CreateValidSGMLID ($field_name);
2022         $condition = &MakeConditionDescription ($field_name);
2023         $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2024         if (defined $field_descr) {
2025             ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2026             $field_descr = &ConvertMarkDown($symbol, $field_descr);
2027             $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2028             delete $field_descrs{$field_name};
2029         } else {
2030             if ($found) {
2031                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2032                     "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2033                 if ($missing_parameters ne "") {
2034                     $missing_parameters .= ", ".$field_name;
2035                 } else {
2036                     $missing_parameters = $field_name;
2037                 }
2038             }
2039             $desc .= "<entry /><entry />\n";
2040         }
2041         $desc .= "</row>\n";
2042     }
2043     $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2044     foreach my $field_name (keys %field_descrs) {
2045         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2046             "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2047         if ($unused_parameters ne "") {
2048             $unused_parameters .= ", ".$field_name;
2049         } else {
2050             $unused_parameters = $field_name;
2051         }
2052     }
2054     # remember missing/unused parameters (needed in tmpl-free build)
2055     if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2056         $AllIncompleteSymbols{$symbol}=$missing_parameters;
2057     }
2058     if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2059         $AllUnusedSymbols{$symbol}=$unused_parameters;
2060     }
2062     if (!$found) {
2063         if (scalar(@fields) > 0) {
2064             if (! exists ($AllIncompleteSymbols{$symbol})) {
2065                 $AllIncompleteSymbols{$symbol}="<items>";
2066                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2067                     "Value descriptions for $symbol are missing in source code comment block.");
2068             }
2069         }
2070     }
2072     $desc .= OutputSymbolTraits ($symbol);
2073     $desc .= "</refsect2>\n";
2074     return ($synop, $desc);
2078 #############################################################################
2079 # Function    : OutputVariable
2080 # Description : Returns the synopsis and detailed description of a variable.
2081 # Arguments   : $symbol - the extern'ed variable.
2082 #                $declaration - the declaration of the variable.
2083 #############################################################################
2085 sub OutputVariable {
2086     my ($symbol, $declaration) = @_;
2087     my $id = &CreateValidSGMLID ($symbol);
2088     my $condition = &MakeConditionDescription ($symbol);
2089     
2090     @TRACE@("ouputing variable: '$symbol' '$declaration'");
2092     my $type_output;
2093     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*;/) {
2094         my $mod1 = defined ($1) ? $1 : "";
2095         my $ptr = defined ($3) ? $3 : "";
2096         my $space = defined ($4) ? $4 : "";
2097         my $mod2 = defined ($5) ? $5 : "";
2098         $type_output = "extern $mod1$ptr$space$mod2";
2099     } 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*=/) {
2100         my $mod1 = defined ($1) ? $1 : "";
2101         my $ptr = defined ($3) ? $3 : "";
2102         my $space = defined ($4) ? $4 : "";
2103         my $mod2 = defined ($5) ? $5 : "";
2104         $type_output = "$mod1$ptr$space$mod2";
2105     } else {
2106         $type_output = "extern";
2107     }
2108     my $synop = "<row><entry role=\"c_punctuation\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2110     my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2112     $desc .= MakeIndexterms($symbol, $id);
2113     $desc .= "\n";
2114     $desc .= OutputSymbolExtraLinks($symbol);
2116     my $decl_out = &CreateValidSGML ($declaration);
2117     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2119     $desc .= &MakeDeprecationNote($symbol);
2121     if (defined ($SymbolDocs{$symbol})) {
2122         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2123     }
2124     $desc .= OutputSymbolTraits ($symbol);
2125     $desc .= "</refsect2>\n";
2126     return ($synop, $desc);
2130 #############################################################################
2131 # Function    : OutputFunction
2132 # Description : Returns the synopsis and detailed description of a function.
2133 # Arguments   : $symbol - the function.
2134 #                $declaration - the declaration of the function.
2135 #############################################################################
2137 sub OutputFunction {
2138     my ($symbol, $declaration, $symbol_type) = @_;
2139     my $id = &CreateValidSGMLID ($symbol);
2140     my $condition = &MakeConditionDescription ($symbol);
2142     # Take out the return type     $1                                                                                       $2   $3
2143     $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//;
2144     my $type_modifier = defined($1) ? $1 : "";
2145     my $type = $2;
2146     my $pointer = $3;
2147     # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2148     $pointer =~ s/\s+$//;
2149     my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2150     my $start = "";
2151     #if ($symbol_type eq 'USER_FUNCTION') {
2152     #    $start = "typedef ";
2153     #}
2155     # We output const rather than G_CONST_RETURN.
2156     $type_modifier =~ s/G_CONST_RETURN/const/g;
2157     $pointer =~ s/G_CONST_RETURN/const/g;
2158     $pointer =~ s/^\s+/&#160;/g;
2160     my $ret_type_output;
2161     $ret_type_output = "$start$type_modifier$xref$pointer\n";
2163     my $indent_len;
2164     $indent_len = length ($symbol) + 2;
2165     my $char1 = my $char2 = my $char3 = "";
2166     if ($symbol_type eq 'USER_FUNCTION') {
2167         $indent_len += 3;
2168         $char1 = "(";
2169         $char2 = "*";
2170         $char3 = ")";
2171     }
2173     my ($symbol_output, $symbol_desc_output);
2174     $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2175     if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2176         $symbol_desc_output = "$char1$char2$symbol$char3 ";
2177     } else {
2178         $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2179         $symbol_desc_output = "$char1$char2$symbol$char3\n"
2180           . (' ' x ($indent_len - 1));
2181     }
2183     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";
2185     my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol}&#160;<phrase role=\"c_punctuation\">()</phrase></title>\n";
2187     $desc .= MakeIndexterms($symbol, $id);
2188     $desc .= "\n";
2189     $desc .= OutputSymbolExtraLinks($symbol);
2191     $desc  .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2193     my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2194                                         sub {
2195                                             &tagify($_[0],"parameter");
2196                                         });
2198     for (my $i = 1; $i <= $#fields; $i += 2) {
2199         my $field_name = $fields[$i];
2201         if ($i == 1) {
2202             $desc  .= "$field_name";
2203         } else {
2204             $desc  .= ",\n"
2205                 . (' ' x $indent_len)
2206                 . "$field_name";
2207         }
2209     }
2211     $desc  .= ");</programlisting>\n";
2213     $desc .= &MakeDeprecationNote($symbol);
2215     my $parameters = &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2216     my $parameters_output = 0;
2218     if (defined ($SymbolDocs{$symbol})) {
2219         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2221         # Try to insert the parameter table at the author's desired position.
2222         # Otherwise we need to tag it onto the end.
2223         # FIXME: document that in the user manual and make it useable for other
2224         # types too
2225         if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
2226           $parameters_output = 1;
2227         }
2228         $desc .= $symbol_docs;
2229     }
2231     if ($parameters_output == 0) {
2232         $desc .= $parameters;
2233     }
2235     $desc .= OutputSymbolTraits ($symbol);
2236     $desc .= "</refsect2>\n";
2237     return ($synop, $desc);
2241 #############################################################################
2242 # Function    : OutputParamDescriptions
2243 # Description : Returns the DocBook output describing the parameters of a
2244 #                function, macro or signal handler.
2245 # Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2246 #                  handlers have an implicit user_data parameter last.
2247 #                $symbol - the name of the function/macro being described.
2248 #               @fields - parsed fields from the declaration, used to determine
2249 #                  undocumented/unused entries
2250 #############################################################################
2252 sub OutputParamDescriptions {
2253     my ($symbol_type, $symbol, @fields) = @_;
2254     my $output = "";
2255     my $params = $SymbolParams{$symbol};
2256     my $num_params = 0;
2257     my %field_descrs = ();
2259     if (@fields) {
2260         %field_descrs = @fields;
2261         delete $field_descrs{"void"};
2262         delete $field_descrs{"Returns"};
2263     }
2265     if (defined $params) {
2266         my $returns = "";
2267         my $params_desc = "";
2268         my $missing_parameters = "";
2269         my $unused_parameters = "";
2270         my $j;
2272         for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2273             my $param_name = $$params[$j];
2274             my $param_desc = $$params[$j + 1];
2275             my $param_annotations = "";
2277             ($param_desc,$param_annotations) = & ExpandAnnotation($symbol, $param_desc);
2278             $param_desc = &ConvertMarkDown($symbol, $param_desc);
2279             # trim
2280             $param_desc =~ s/^(\s|\n)+//msg;
2281             $param_desc =~ s/(\s|\n)+$//msg;
2282             if ($param_name eq "Returns") {
2283                 $returns = "$param_desc\n<para>$param_annotations</para>";
2284             } elsif ($param_name eq "void") {
2285                 #print "!!!! 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     #print "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         #print "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         #print "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         #print "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         #print "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         #print "Found stability: $stability";
2462     }
2463     if ($stability) {
2464         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n$stability, unless otherwise indicated\n</refsect1>\n";
2465     } elsif ($DEFAULT_STABILITY) {
2466         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n$DEFAULT_STABILITY, unless otherwise indicated\n</refsect1>\n";
2467     }
2469     my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2470     if (!defined ($image) || $image =~ m/^\s*$/) {
2471       $image = "";
2472     } else {
2473       $image =~ s/^\s*//;
2474       $image =~ s/\s*$//;
2476       my $format;
2478       if ($image =~ /jpe?g$/i) {
2479         $format = "format='JPEG'";
2480       } elsif ($image =~ /png$/i) {
2481         $format = "format='PNG'";
2482       } elsif ($image =~ /svg$/i) {
2483         $format = "format='SVG'";
2484       } else {
2485         $format = "";
2486       }
2488       $image = "  <inlinegraphic fileref='$image' $format/>\n"
2489     }
2491     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2492         gmtime (time);
2493     my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2494     $year += 1900;
2496     my $include_output = "";
2497     if ($includes) {
2498       $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2499       my $include;
2500       foreach $include (split (/,/, $includes)) {
2501         if ($include =~ m/^\".+\"$/) {
2502           $include_output .= "#include ${include}\n";
2503         }
2504         else {
2505           $include =~ s/^\s+|\s+$//gs;
2506           $include_output .= "#include &lt;${include}&gt;\n";
2507         }
2508       }
2509       $include_output .= "</synopsis></refsect1>\n";
2510     }
2512     my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2514     my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT";
2515     my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new";
2517     open (OUTPUT, ">$new_sgml_file")
2518         || die "Can't create $new_sgml_file: $!";
2520     my $object_anchors = "";
2521     foreach my $object (@$file_objects) {
2522         next if ($object eq $section_id);
2523         my $id = CreateValidSGMLID($object);
2524         #print "Debug: Adding anchor for $object\n";
2525         $object_anchors .= "<anchor id=\"$id\"$empty_element_end";
2526     }
2528     # We used to output this, but is messes up our UpdateFileIfChanged code
2529     # since it changes every day (and it is only used in the man pages):
2530     # "<refentry id="$section_id" revision="$mday $month $year">"
2532     if ($OUTPUT_FORMAT eq "xml") {
2533         print OUTPUT $doctype_header;
2534     }
2536     print OUTPUT <<EOF;
2537 <refentry id="$section_id">
2538 <refmeta>
2539 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2540 <manvolnum>3</manvolnum>
2541 <refmiscinfo>
2542   \U$MODULE\E Library
2543 $image</refmiscinfo>
2544 </refmeta>
2545 <refnamediv>
2546 <refname>$title</refname>
2547 <refpurpose>$short_desc</refpurpose>
2548 </refnamediv>
2549 $stability
2550 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2551 $include_output
2552 <refsect1 id="$section_id.description" role="desc">
2553 <title role="desc.title">Description</title>
2554 $extralinks$long_desc
2555 </refsect1>
2556 <refsect1 id="$section_id.functions_details" role="details">
2557 <title role="details.title">Functions</title>
2558 $$functions_details
2559 </refsect1>
2560 <refsect1 id="$section_id.other_details" role="details">
2561 <title role="details.title">Types and Values</title>
2562 $$other_details
2563 </refsect1>
2564 $$args_desc$$signals_desc$see_also
2565 </refentry>
2567     close (OUTPUT);
2569     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2573 #############################################################################
2574 # Function    : OutputExtraFile
2575 # Description : Copies an "extra" DocBook file into the output directory,
2576 #               expanding abbreviations
2577 # Arguments   : $file - the source file.
2578 #############################################################################
2579 sub OutputExtraFile {
2580     my ($file) = @_;
2582     my $basename;
2584     ($basename = $file) =~ s!^.*/!!;
2586     my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename";
2587     my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new";
2589     my $contents;
2591     open(EXTRA_FILE, "<$file") || die "Can't open $file";
2593     {
2594         local $/;
2595         $contents = <EXTRA_FILE>;
2596     }
2598     open (OUTPUT, ">$new_sgml_file")
2599         || die "Can't create $new_sgml_file: $!";
2601     print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2602     close (OUTPUT);
2604     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2606 #############################################################################
2607 # Function    : OutputBook
2608 # Description : Outputs the SGML entities that need to be included into the
2609 #                main SGML file for the module.
2610 # Arguments   : $book_top - the declarations of the entities, which are added
2611 #                  at the top of the main SGML file.
2612 #                $book_bottom - the references to the entities, which are
2613 #                  added in the main SGML file at the desired position.
2614 #############################################################################
2616 sub OutputBook {
2617     my ($book_top, $book_bottom) = @_;
2619     my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top";
2620     my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new";
2622     open (OUTPUT, ">$new_file")
2623         || die "Can't create $new_file: $!";
2624     print OUTPUT $book_top;
2625     close (OUTPUT);
2627     &UpdateFileIfChanged ($old_file, $new_file, 0);
2630     $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom";
2631     $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new";
2633     open (OUTPUT, ">$new_file")
2634         || die "Can't create $new_file: $!";
2635     print OUTPUT $book_bottom;
2636     close (OUTPUT);
2638     &UpdateFileIfChanged ($old_file, $new_file, 0);
2641     # If the main SGML/XML file hasn't been created yet, we create it here.
2642     # The user can tweak it later.
2643     if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2644       open (OUTPUT, ">$MAIN_SGML_FILE")
2645         || die "Can't create $MAIN_SGML_FILE: $!";
2647       if ($OUTPUT_FORMAT eq "xml") {
2648           print OUTPUT <<EOF;
2649 <?xml version="1.0"?>
2650 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2651                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2653   <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
2655 <book id="index">
2657       } else {
2658         print OUTPUT <<EOF;
2659 <!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
2660 $book_top
2662 <book id="index">
2664       }
2666 print OUTPUT <<EOF;
2667   <bookinfo>
2668     <title>$MODULE Reference Manual</title>
2669     <releaseinfo>
2670       for $MODULE [VERSION].
2671       The latest version of this documentation can be found on-line at
2672       <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2673     </releaseinfo>
2674   </bookinfo>
2676   <chapter>
2677     <title>[Insert title here]</title>
2678     $book_bottom
2679   </chapter>
2681   if (-e $OBJECT_TREE_FILE) {
2682     print OUTPUT <<EOF;
2683   <chapter id="object-tree">
2684     <title>Object Hierarchy</title>
2685      <xi:include href="xml/tree_index.sgml"/>
2686   </chapter>
2688   }
2690 print OUTPUT <<EOF;
2691   <index id="api-index-full">
2692     <title>API Index</title>
2693     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2694   </index>
2695   <index id="deprecated-api-index" role="deprecated">
2696     <title>Index of deprecated API</title>
2697     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2698   </index>
2700   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2701 </book>
2704       close (OUTPUT);
2705     }
2709 #############################################################################
2710 # Function    : CreateValidSGML
2711 # Description : This turns any chars which are used in SGML into entities,
2712 #                e.g. '<' into '&lt;'
2713 # Arguments   : $text - the text to turn into proper SGML.
2714 #############################################################################
2716 sub CreateValidSGML {
2717     my ($text) = @_;
2718     $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
2719     $text =~ s/</&lt;/g;
2720     $text =~ s/>/&gt;/g;
2721     # browers render single tabs inconsistently
2722     $text =~ s/([^\s])\t([^\s])/$1&#160;$2/g;
2723     return $text;
2726 #############################################################################
2727 # Function    : ConvertSGMLChars
2728 # Description : This is used for text in source code comment blocks, to turn
2729 #               chars which are used in SGML into entities, e.g. '<' into
2730 #               '&lt;'. Depending on $INLINE_MARKUP_MODE, this is done
2731 #               unconditionally or only if the character doesn't seem to be
2732 #               part of an SGML construct (tag or entity reference).
2733 # Arguments   : $text - the text to turn into proper SGML.
2734 #############################################################################
2736 sub ConvertSGMLChars {
2737     my ($symbol, $text) = @_;
2739     if ($INLINE_MARKUP_MODE) {
2740         # For the XML/SGML mode only convert to entities outside CDATA sections.
2741         return &ModifyXMLElements ($text, $symbol,
2742                                    "<!\\[CDATA\\[|<programlisting[^>]*>",
2743                                    \&ConvertSGMLCharsEndTag,
2744                                    \&ConvertSGMLCharsCallback);
2745     } else {
2746         # For the simple non-sgml mode, convert to entities everywhere.
2747         $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
2748         $text =~ s/</&lt;/g;
2749         $text =~ s/>/&gt;/g;
2750         return $text;
2751     }
2755 sub ConvertSGMLCharsEndTag {
2756   if ($_[0] eq "<!\[CDATA\[") {
2757     return "]]>";
2758   } else {
2759     return "</programlisting>";
2760   }
2763 sub ConvertSGMLCharsCallback {
2764   my ($text, $symbol, $tag) = @_;
2766   if ($tag =~ m/^<programlisting/) {
2767     # We can handle <programlisting> specially here.
2768     return &ModifyXMLElements ($text, $symbol,
2769                                "<!\\[CDATA\\[",
2770                                \&ConvertSGMLCharsEndTag,
2771                                \&ConvertSGMLCharsCallback2);
2772   } elsif ($tag eq "") {
2773     # If we're not in CDATA convert to entities.
2774     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2775     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2776     # Allow ">" at beginning of string for blockquote markdown
2777     $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2779     # Handle "#include <xxxxx>"
2780     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2781   }
2783   return $text;
2786 sub ConvertSGMLCharsCallback2 {
2787   my ($text, $symbol, $tag) = @_;
2789   # If we're not in CDATA convert to entities.
2790   # We could handle <programlisting> differently, though I'm not sure it helps.
2791   if ($tag eq "") {
2792     # replace only if its not a tag
2793     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2794     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2795     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
2797     # Handle "#include <xxxxx>"
2798     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2799   }
2801   return $text;
2804 #############################################################################
2805 # Function    : ExpandAnnotation
2806 # Description : This turns annotations into acronym tags.
2807 # Arguments   : $symbol - the symbol being documented, for error messages.
2808 #                $text - the text to expand.
2809 #############################################################################
2810 sub ExpandAnnotation {
2811     my ($symbol, $param_desc) = @_;
2812     my $param_annotations = "";
2814     # look for annotations at the start of the comment part
2815     if ($param_desc =~ m%^\s*\((.*?)\):%) {
2816         my @annotations;
2817         my $annotation;
2818         $param_desc = $';
2820         @annotations = split(/\)\s*\(/,$1);
2821         foreach $annotation (@annotations) {
2822             # need to search for the longest key-match in %AnnotationDefinition
2823             my $match_length=0;
2824             my $match_annotation="";
2825             my $annotationdef;
2826             foreach $annotationdef (keys %AnnotationDefinition) {
2827                 if ($annotation =~ m/^$annotationdef/) {
2828                     if (length($annotationdef)>$match_length) {
2829                         $match_length=length($annotationdef);
2830                         $match_annotation=$annotationdef;
2831                     }
2832                 }
2833             }
2834             my $annotation_extra = "";
2835             if ($match_annotation ne "") {
2836                 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2837                     $annotation_extra = " $1";
2838                 }
2839                 $AnnotationsUsed{$match_annotation} = 1;
2840                 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2841             }
2842             else {
2843                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2844                     "unknown annotation \"$annotation\" in documentation for $symbol.");
2845                 $param_annotations .= "[$annotation]";
2846             }
2847         }
2848         chomp($param_desc);
2849         $param_desc =~ m/^(.*?)\.*\s*$/s;
2850         $param_desc = "$1. ";
2851     }
2852     if ($param_annotations ne "") {
2853         $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2854     }
2855     return ($param_desc, $param_annotations);
2858 #############################################################################
2859 # Function    : ExpandAbbreviations
2860 # Description : This turns the abbreviations function(), macro(), @param,
2861 #                %constant, and #symbol into appropriate DocBook markup.
2862 #               CDATA sections and <programlisting> parts are skipped.
2863 # Arguments   : $symbol - the symbol being documented, for error messages.
2864 #                $text - the text to expand.
2865 #############################################################################
2867 sub ExpandAbbreviations {
2868   my ($symbol, $text) = @_;
2870   # Note: This is a fallback and normally done in the markdown parser
2872   # Convert "|[" and "]|" into the start and end of program listing examples.
2873   # Support \[<!-- language="C" --> modifiers
2874   $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2875   $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2876   $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2878   # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2879   # as such)
2880   return &ModifyXMLElements ($text, $symbol,
2881                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2882                              \&ExpandAbbreviationsEndTag,
2883                              \&ExpandAbbreviationsCallback);
2887 # Returns the end tag (as a regexp) corresponding to the given start tag.
2888 sub ExpandAbbreviationsEndTag {
2889   my ($start_tag) = @_;
2891   if ($start_tag eq "<!\[CDATA\[") {
2892     return "]]>";
2893   } elsif ($start_tag eq "<!DOCTYPE") {
2894     return ">";
2895   } elsif ($start_tag =~ m/<(\w+)/) {
2896     return "</$1>";
2897   }
2900 # Called inside or outside each CDATA or <programlisting> section.
2901 sub ExpandAbbreviationsCallback {
2902   my ($text, $symbol, $tag) = @_;
2904   if ($tag =~ m/^<programlisting/) {
2905     # Handle any embedded CDATA sections.
2906     return &ModifyXMLElements ($text, $symbol,
2907                                "<!\\[CDATA\\[",
2908                                \&ExpandAbbreviationsEndTag,
2909                                \&ExpandAbbreviationsCallback2);
2910   } elsif ($tag eq "") {
2911     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2913     # We are outside any CDATA or <programlisting> sections, so we expand
2914     # any gtk-doc abbreviations.
2916     # Convert '@param()'
2917     # FIXME: we could make those also links ($symbol.$2), but that would be less
2918     # useful as the link target is a few lines up or down
2919     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2921     # Convert 'function()' or 'macro()'.
2922     # if there is abc_*_def() we don't want to make a link to _def()
2923     # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2924     $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2925     # handle #Object.func()
2926     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2928     # Convert '@param', but not '\@param'.
2929     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2930     $text =~ s/\\\@/\@/g;
2932     # Convert '%constant', but not '\%constant'.
2933     # Also allow negative numbers, e.g. %-1.
2934     $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2935     $text =~ s/\\\%/\%/g;
2937     # Convert '#symbol', but not '\#symbol'.
2938     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2939     $text =~ s/\\#/#/g;
2940   }
2942   return $text;
2945 # This is called inside a <programlisting>
2946 sub ExpandAbbreviationsCallback2 {
2947   my ($text, $symbol, $tag) = @_;
2949   if ($tag eq "") {
2950     # We are inside a <programlisting> but outside any CDATA sections,
2951     # so we expand any gtk-doc abbreviations.
2952     # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2953     #        why not just call it
2954     $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2955   } elsif ($tag eq "<![CDATA[") {
2956     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2957     $text = &ReplaceEntities ($text, $symbol);
2958   }
2960   return $text;
2963 sub MakeHashXRef {
2964     my ($symbol, $tag) = @_;;
2965     my $text = $symbol;
2967     # Check for things like '#include', '#define', and skip them.
2968     if ($PreProcessorDirectives{$symbol}) {
2969       return "#$symbol";
2970     }
2972     # Get rid of special suffixes ('-struct','-enum').
2973     $text =~ s/-struct$//;
2974     $text =~ s/-enum$//;
2976     # If the symbol is in the form "Object::signal", then change the symbol to
2977     # "Object-signal" and use "signal" as the text.
2978     if ($symbol =~ s/::/-/) {
2979       $text = "“$'”";
2980     }
2982     # If the symbol is in the form "Object:property", then change the symbol to
2983     # "Object--property" and use "property" as the text.
2984     if ($symbol =~ s/:/--/) {
2985       $text = "“$'”";
2986     }
2988     if ($tag ne "") {
2989       $text = tagify ($text, $tag);
2990     }
2992     return &MakeXRef($symbol, $text);
2996 #############################################################################
2997 # Function    : ModifyXMLElements
2998 # Description : Looks for given XML element tags within the text, and calls
2999 #               the callback on pieces of text inside & outside those elements.
3000 #               Used for special handling of text inside things like CDATA
3001 #               and <programlisting>.
3002 # Arguments   : $text - the text.
3003 #               $symbol - the symbol currently being documented (only used for
3004 #                      error messages).
3005 #               $start_tag_regexp - the regular expression to match start tags.
3006 #                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3007 #                      CDATA sections or programlisting elements.
3008 #               $end_tag_func - function which is passed the matched start tag
3009 #                      and should return the appropriate end tag string regexp.
3010 #               $callback - callback called with each part of the text. It is
3011 #                      called with a piece of text, the symbol being
3012 #                      documented, and the matched start tag or "" if the text
3013 #                      is outside the XML elements being matched.
3014 #############################################################################
3015 sub ModifyXMLElements {
3016     my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3017     my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3018     my $result = "";
3020     while ($text =~ m/$start_tag_regexp/s) {
3021       $before_tag = $`; # Prematch for last successful match string
3022       $start_tag = $&;  # Last successful match
3023       $text = $';       # Postmatch for last successful match string
3025       $result .= &$callback ($before_tag, $symbol, "");
3026       $result .= $start_tag;
3028       # get the matching end-tag for current tag
3029       $end_tag_regexp = &$end_tag_func ($start_tag);
3031       if ($text =~ m/$end_tag_regexp/s) {
3032         $before_tag = $`;
3033         $end_tag = $&;
3034         $text = $';
3036         $result .= &$callback ($before_tag, $symbol, $start_tag);
3037         $result .= $end_tag;
3038       } else {
3039         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3040             "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3041         # Just assume it is all inside the tag.
3042         $result .= &$callback ($text, $symbol, $start_tag);
3043         $text = "";
3044       }
3045     }
3047     # Handle any remaining text outside the tags.
3048     $result .= &$callback ($text, $symbol, "");
3050     return $result;
3053 sub noop {
3054   return $_[0];
3057 # Adds a tag around some text.
3058 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3059 sub tagify {
3060    my ($text, $elem) = @_;
3061    return "<" . $elem . ">" . $text . "</" . $elem . ">";
3065 #############################################################################
3066 # Function    : MakeXRef
3067 # Description : This returns a cross-reference link to the given symbol.
3068 #                Though it doesn't try to do this for a few standard C types
3069 #                that it        knows won't be in the documentation.
3070 # Arguments   : $symbol - the symbol to try to create a XRef to.
3071 #               $text - text text to put inside the XRef, defaults to $symbol
3072 #############################################################################
3074 sub MakeXRef {
3075     my ($symbol, $text) = ($_[0], $_[1]);
3077     $symbol =~ s/^\s+//;
3078     $symbol =~ s/\s+$//;
3080     if (!defined($text)) {
3081         $text = $symbol;
3083         # Get rid of special suffixes ('-struct','-enum').
3084         $text =~ s/-struct$//;
3085         $text =~ s/-enum$//;
3086     }
3088     if ($symbol =~ m/ /) {
3089         return "$text";
3090     }
3092     #print "Getting type link for $symbol -> $text\n";
3094     my $symbol_id = &CreateValidSGMLID ($symbol);
3095     return "<link linkend=\"$symbol_id\">$text</link>";
3099 #############################################################################
3100 # Function    : MakeIndexterms
3101 # Description : This returns a indexterm elements for the given symbol
3102 # Arguments   : $symbol - the symbol to create indexterms for
3103 #############################################################################
3105 sub MakeIndexterms {
3106   my ($symbol, $id) = @_;
3107   my $terms =  "";
3108   my $sortas = "";
3110   # make the index useful, by ommiting the namespace when sorting
3111   if ($NAME_SPACE ne "") {
3112     if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3113        $sortas=" sortas=\"$1\"";
3114     }
3115   }
3117   if (exists $Deprecated{$symbol}) {
3118       $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3119       $IndexEntriesDeprecated{$symbol}=$id;
3120       $IndexEntriesFull{$symbol}=$id;
3121   }
3122   if (exists $Since{$symbol}) {
3123      my $since = $Since{$symbol};
3124      $since =~ s/^\s+//;
3125      $since =~ s/\s+$//;
3126      if ($since ne "") {
3127          $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3128      }
3129      $IndexEntriesSince{$symbol}=$id;
3130      $IndexEntriesFull{$symbol}=$id;
3131   }
3132   if ($terms eq "") {
3133      $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3134      $IndexEntriesFull{$symbol}=$id;
3135   }
3137   return $terms;
3140 #############################################################################
3141 # Function    : MakeDeprecationNote
3142 # Description : This returns a deprecation warning for the given symbol.
3143 # Arguments   : $symbol - the symbol to try to create a warning for.
3144 #############################################################################
3146 sub MakeDeprecationNote {
3147     my ($symbol) = $_[0];
3148     my $desc = "";
3149     if (exists $Deprecated{$symbol}) {
3150         my $note;
3152         $desc .= "<warning><para><literal>$symbol</literal> ";
3154         $note = $Deprecated{$symbol};
3156         if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3157                 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3158         } else {
3159                 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3160         }
3161         $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3162         $note =~ s/^\s+//;
3163         $note =~ s/\s+$//;
3164         if ($note ne "") {
3165             $note = &ConvertMarkDown($symbol, $note);
3166             $desc .= " " . $note;
3167         }
3168         $desc .= "</warning>\n";
3169     }
3170     return $desc;
3173 #############################################################################
3174 # Function    : MakeConditionDescription
3175 # Description : This returns a sumary of conditions for the given symbol.
3176 # Arguments   : $symbol - the symbol to try to create the sumary.
3177 #############################################################################
3179 sub MakeConditionDescription {
3180     my ($symbol) = $_[0];
3181     my $desc = "";
3183     if (exists $Deprecated{$symbol}) {
3184         if ($desc ne "") {
3185             $desc .= "|";
3186         }
3188         if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3189                 $desc .= "deprecated:$1";
3190         } else {
3191                 $desc .= "deprecated";
3192         }
3193     }
3195     if (exists $Since{$symbol}) {
3196         if ($desc ne "") {
3197             $desc .= "|";
3198         }
3200         if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3201                 $desc .= "since:$1";
3202         } else {
3203                 $desc .= "since";
3204         }
3205     }
3207     if (exists $StabilityLevel{$symbol}) {
3208         if ($desc ne "") {
3209             $desc .= "|";
3210         }
3211         $desc .= "stability:".$StabilityLevel{$symbol};
3212     }
3214     if ($desc ne "") {
3215         my $cond = $desc;
3216         $cond =~ s/\"/&quot;/g;
3217         $desc=" condition=\"".$cond."\"";
3218         #print "condition for '$symbol' = '$desc'\n";
3219     }
3220     return $desc;
3223 #############################################################################
3224 # Function    : GetHierarchy
3225 # Description : Returns the DocBook output describing the ancestors and
3226 #               immediate children of a GObject subclass. It uses the
3227 #               global @Objects and @ObjectLevels arrays to walk the tree.
3228 # Arguments   : $object - the GtkObject subclass.
3229 #############################################################################
3231 sub GetHierarchy {
3232     my ($object) = @_;
3234     # Find object in the objects array.
3235     my $found = 0;
3236     my @children = ();
3237     my $i;
3238     my $level;
3239     my $j;
3240     for ($i = 0; $i < @Objects; $i++) {
3241         if ($found) {
3242             if ($ObjectLevels[$i] <= $level) {
3243             last;
3244         }
3245             elsif ($ObjectLevels[$i] == $level + 1) {
3246                 push (@children, $Objects[$i]);
3247             }
3248         }
3249         elsif ($Objects[$i] eq $object) {
3250             $found = 1;
3251             $j = $i;
3252             $level = $ObjectLevels[$i];
3253         }
3254     }
3255     if (!$found) {
3256         return "";
3257     }
3259     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3260     my @ancestors = ();
3261     push (@ancestors, $object);
3262     #print "Level: $level\n";
3263     while ($level > 1) {
3264         $j--;
3265         if ($ObjectLevels[$j] < $level) {
3266             push (@ancestors, $Objects[$j]);
3267             $level = $ObjectLevels[$j];
3268             #print "Level: $level\n";
3269         }
3270     }
3272     # Output the ancestors list, indented and with links.
3273     my $hierarchy = "<synopsis>\n";
3274     $level = 0;
3275     for ($i = $#ancestors; $i >= 0; $i--) {
3276         my $link_text;
3277         # Don't add a link to the current object, i.e. when i == 0.
3278         if ($i > 0) {
3279             my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]);
3280             $link_text = "<link linkend=\"$ancestor_id\">$ancestors[$i]</link>";
3281         } else {
3282             $link_text = "$ancestors[$i]";
3283         }
3284         if ($level == 0) {
3285             $hierarchy .= " $link_text\n";
3286         } else {
3287             # Unicode chars for: ╰───
3288             $hierarchy .= ' ' x ($level * 4 - 3) . "<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase> $link_text\n";
3289         }
3290         $level++;
3291     }
3292     for ($i = 0; $i <= $#children; $i++) {
3293       my $id = &CreateValidSGMLID ($children[$i]);
3294       my $link_text = "<link linkend=\"$id\">$children[$i]</link>";
3295       my $junction = "&#9500;"; # unicde for ├
3296       if ($i == $#children) {
3297         $junction = "&#9584;"; # unicode for ╰
3298       }
3299       $hierarchy .= ' ' x ($level * 4 - 3) . "<phrase role=\"lineart\">" . $junction . "&#9472;&#9472;</phrase> $link_text\n";
3300     }
3301     $hierarchy .= "</synopsis>\n";
3303     return $hierarchy;
3307 #############################################################################
3308 # Function    : GetInterfaces
3309 # Description : Returns the DocBook output describing the interfaces
3310 #               implemented by a class. It uses the global %Interfaces hash.
3311 # Arguments   : $object - the GtkObject subclass.
3312 #############################################################################
3314 sub GetInterfaces {
3315     my ($object) = @_;
3316     my $text = "";
3317     my $i;
3319     # Find object in the objects array.
3320     if (exists($Interfaces{$object})) {
3321         my @ifaces = split(' ', $Interfaces{$object});
3322         $text = <<EOF;
3323 <para>
3324 $object implements
3326         for ($i = 0; $i <= $#ifaces; $i++) {
3327             my $id = &CreateValidSGMLID ($ifaces[$i]);
3328             $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3329             if ($i < $#ifaces - 1) {
3330                 $text .= ', ';
3331             }
3332             elsif ($i < $#ifaces) {
3333                 $text .= ' and ';
3334             }
3335             else {
3336                 $text .= '.';
3337             }
3338         }
3339         $text .= <<EOF;
3340 </para>
3342     }
3344     return $text;
3347 #############################################################################
3348 # Function    : GetImplementations
3349 # Description : Returns the DocBook output describing the implementations
3350 #               of an interface. It uses the global %Interfaces hash.
3351 # Arguments   : $object - the GtkObject subclass.
3352 #############################################################################
3354 sub GetImplementations {
3355     my ($object) = @_;
3356     my @impls = ();
3357     my $text = "";
3358     my $i;
3359     foreach my $key (keys %Interfaces) {
3360         if ($Interfaces{$key} =~ /\b$object\b/) {
3361             push (@impls, $key);
3362         }
3363     }
3364     if ($#impls >= 0) {
3365         @impls = sort @impls;
3366         $text = <<EOF;
3367 <para>
3368 $object is implemented by
3370         for ($i = 0; $i <= $#impls; $i++) {
3371             my $id = &CreateValidSGMLID ($impls[$i]);
3372             $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3373             if ($i < $#impls - 1) {
3374                 $text .= ', ';
3375             }
3376             elsif ($i < $#impls) {
3377                 $text .= ' and ';
3378             }
3379             else {
3380                 $text .= '.';
3381             }
3382         }
3383         $text .= <<EOF;
3384 </para>
3386     }
3387     return $text;
3391 #############################################################################
3392 # Function    : GetPrerequisites
3393 # Description : Returns the DocBook output describing the prerequisites
3394 #               of an interface. It uses the global %Prerequisites hash.
3395 # Arguments   : $iface - the interface.
3396 #############################################################################
3398 sub GetPrerequisites {
3399     my ($iface) = @_;
3400     my $text = "";
3401     my $i;
3403     if (exists($Prerequisites{$iface})) {
3404         $text = <<EOF;
3405 <para>
3406 $iface requires
3408         my @prereqs = split(' ', $Prerequisites{$iface});
3409         for ($i = 0; $i <= $#prereqs; $i++) {
3410             my $id = &CreateValidSGMLID ($prereqs[$i]);
3411             $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3412             if ($i < $#prereqs - 1) {
3413                 $text .= ', ';
3414             }
3415             elsif ($i < $#prereqs) {
3416                 $text .= ' and ';
3417             }
3418             else {
3419                 $text .= '.';
3420             }
3421         }
3422         $text .= <<EOF;
3423 </para>
3425     }
3426     return $text;
3429 #############################################################################
3430 # Function    : GetDerived
3431 # Description : Returns the DocBook output describing the derived interfaces
3432 #               of an interface. It uses the global %Prerequisites hash.
3433 # Arguments   : $iface - the interface.
3434 #############################################################################
3436 sub GetDerived {
3437     my ($iface) = @_;
3438     my $text = "";
3439     my $i;
3441     my @derived = ();
3442     foreach my $key (keys %Prerequisites) {
3443         if ($Prerequisites{$key} =~ /\b$iface\b/) {
3444             push (@derived, $key);
3445         }
3446     }
3447     if ($#derived >= 0) {
3448         @derived = sort @derived;
3449         $text = <<EOF;
3450 <para>
3451 $iface is required by
3453         for ($i = 0; $i <= $#derived; $i++) {
3454             my $id = &CreateValidSGMLID ($derived[$i]);
3455             $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3456             if ($i < $#derived - 1) {
3457                 $text .= ', ';
3458             }
3459             elsif ($i < $#derived) {
3460                 $text .= ' and ';
3461             }
3462             else {
3463                 $text .= '.';
3464             }
3465         }
3466         $text .= <<EOF;
3467 </para>
3469     }
3470     return $text;
3474 #############################################################################
3475 # Function    : GetSignals
3476 # Description : Returns the synopsis and detailed description DocBook output
3477 #                for the signal handlers of a given GtkObject subclass.
3478 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3479 #############################################################################
3481 sub GetSignals {
3482     my ($object) = @_;
3483     my $synop = "";
3484     my $desc = "";
3486     my $i;
3487     for ($i = 0; $i <= $#SignalObjects; $i++) {
3488         if ($SignalObjects[$i] eq $object) {
3489             #print "Found signal: $SignalNames[$i]\n";
3490             my $name = $SignalNames[$i];
3491             my $symbol = "${object}::${name}";
3492             my $id = &CreateValidSGMLID ("$object-$name");
3494             $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3495             $desc .= MakeIndexterms($symbol, $id);
3496             $desc .= "\n";
3497             $desc .= OutputSymbolExtraLinks($symbol);
3499             $desc .= "<programlisting language=\"C\">";
3501             $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3502             my $type_modifier = defined($1) ? $1 : "";
3503             my $type = $2;
3504             my $pointer = $3;
3505             my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3507             my $ret_type_output = "$type_modifier$xref$pointer";
3508             my $callback_name = "user_function";
3509             $desc  .= "${ret_type_output}\n${callback_name} (";
3511             my $indentation = ' ' x (length($callback_name) + 2);
3512             my $pad = $indentation;
3514             my $sourceparams = $SourceSymbolParams{$symbol};
3515             my @params = split ("\n", $SignalPrototypes[$i]);
3516             my $j;
3517             my $l;
3518             my $type_len = length("gpointer");
3519             my $name_len = length("user_data");
3520             # do two passes, the first one is to calculate padding
3521             for ($l = 0; $l < 2; $l++) {
3522                 for ($j = 0; $j <= $#params; $j++) {
3523                     my $param_name;
3524                     # allow alphanumerics, '_', '[' & ']' in param names
3525                     if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3526                         $type = $1;
3527                         $pointer = $2;
3528                         if (defined($sourceparams)) {
3529                             $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3530                         }
3531                         else {
3532                             $param_name = $3;
3533                         }
3534                         if (!defined($param_name)) {
3535                             $param_name = "arg$j";
3536                         }
3537                         if ($l == 0) {
3538                             if (length($type) + length($pointer) > $type_len) {
3539                                 $type_len = length($type) + length($pointer);
3540                             }
3541                             if (length($param_name) > $name_len) {
3542                                 $name_len = length($param_name);
3543                             }
3544                         }
3545                         else {
3546                             $xref = &MakeXRef ($type, &tagify($type, "type"));
3547                             $pad = ' ' x ($type_len - length($type) - length($pointer));
3548                             $desc .= "$xref$pad $pointer${param_name},\n";
3549                             $desc .= $indentation;
3550                         }
3551                     } else {
3552                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3553                              "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3554                     }
3555                 }
3556             }
3557             $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3558             $pad = ' ' x ($type_len - length("gpointer"));
3559             $desc  .= "$xref$pad user_data)";
3560             $desc  .= "</programlisting>\n";
3562             my $flags = $SignalFlags[$i];
3563             my $flags_string = "";
3565             if (defined ($flags)) {
3566               if ($flags =~ m/f/) {
3567                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3568               }
3569               elsif ($flags =~ m/l/) {
3570                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3571               }
3572               elsif ($flags =~ m/c/) {
3573                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3574                 $flags_string = "Cleanup";
3575               }
3576               if ($flags =~ m/r/) {
3577                 if ($flags_string) { $flags_string .= " / "; }
3578                 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3579               }
3580               if ($flags =~ m/d/) {
3581                 if ($flags_string) { $flags_string .= " / "; }
3582                 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3583               }
3584               if ($flags =~ m/a/) {
3585                 if ($flags_string) { $flags_string .= " / "; }
3586                 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3587               }
3588               if ($flags =~ m/h/) {
3589                 if ($flags_string) { $flags_string .= " / "; }
3590                 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3591               }
3592             }
3594             $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";
3596             my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3597             my $parameters_output = 0;
3599             $AllSymbols{$symbol} = 1;
3600             if (defined ($SymbolDocs{$symbol})) {
3601                 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3603                 # Try to insert the parameter table at the author's desired
3604                 # position. Otherwise we need to tag it onto the end.
3605                 if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
3606                   $parameters_output = 1;
3607                 }
3608                 $desc .= $symbol_docs;
3610                 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3611                     $AllDocumentedSymbols{$symbol} = 1;
3612                 }
3613             }
3614             $desc .= &MakeDeprecationNote($symbol);
3616             if ($parameters_output == 0) {
3617                 $desc .= $parameters;
3618             }
3619             if ($flags_string) {
3620                 $desc  .= "<para>Flags: $flags_string</para>\n";
3621             }
3622             $desc .= OutputSymbolTraits ($symbol);
3623             $desc .= "</refsect2>";
3624         }
3625     }
3626     return ($synop, $desc);
3630 #############################################################################
3631 # Function    : GetArgs
3632 # Description : Returns the synopsis and detailed description DocBook output
3633 #                for the Args of a given GtkObject subclass.
3634 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3635 #############################################################################
3637 sub GetArgs {
3638     my ($object) = @_;
3639     my $synop = "";
3640     my $desc = "";
3641     my $child_synop = "";
3642     my $child_desc = "";
3643     my $style_synop = "";
3644     my $style_desc = "";
3646     my $i;
3647     for ($i = 0; $i <= $#ArgObjects; $i++) {
3648         if ($ArgObjects[$i] eq $object) {
3649             #print "Found arg: $ArgNames[$i]\n";
3650             my $name = $ArgNames[$i];
3651             my $flags = $ArgFlags[$i];
3652             my $flags_string = "";
3653             my $kind = "";
3654             my $id_sep = "";
3656             if ($flags =~ m/c/) {
3657                 $kind = "child property";
3658                 $id_sep = "c-";
3659             }
3660             elsif ($flags =~ m/s/) {
3661                 $kind = "style property";
3662                 $id_sep = "s-";
3663             }
3664             else {
3665                 $kind = "property";
3666             }
3668             # Remember only one colon so we don't clash with signals.
3669             my $symbol = "${object}:${name}";
3670             # use two dashes and ev. an extra separator here for the same reason.
3671             my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3673             my $type = $ArgTypes[$i];
3674             my $type_output;
3675             my $range = $ArgRanges[$i];
3676             my $range_output = CreateValidSGML ($range);
3677             my $default = $ArgDefaults[$i];
3678             my $default_output = CreateValidSGML ($default);
3680             if ($type eq "GtkString") {
3681                 $type = "char&#160;*";
3682             }
3683             if ($type eq "GtkSignal") {
3684                 $type = "GtkSignalFunc, gpointer";
3685                 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3686                     . &MakeXRef ("gpointer");
3687             } elsif ($type =~ m/^(\w+)\*$/) {
3688                 $type_output = &MakeXRef ($1, &tagify($1, "type")) . "&#160;*";
3689             } else {
3690                 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3691             }
3693             if ($flags =~ m/r/) {
3694                 $flags_string = "Read";
3695             }
3696             if ($flags =~ m/w/) {
3697                 if ($flags_string) { $flags_string .= " / "; }
3698                 $flags_string .= "Write";
3699             }
3700             if ($flags =~ m/x/) {
3701                 if ($flags_string) { $flags_string .= " / "; }
3702                 $flags_string .= "Construct";
3703             }
3704             if ($flags =~ m/X/) {
3705                 if ($flags_string) { $flags_string .= " / "; }
3706                 $flags_string .= "Construct Only";
3707             }
3709             $AllSymbols{$symbol} = 1;
3710             my $blurb;
3711             if (defined($SymbolDocs{$symbol}) &&
3712                 !IsEmptyDoc($SymbolDocs{$symbol})) {
3713                 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3714                 #print ".. [$SymbolDocs{$symbol}][$blurb]\n";
3715                 $AllDocumentedSymbols{$symbol} = 1;
3716             }
3717             else {
3718                 if (!($ArgBlurbs[$i] eq "")) {
3719                     $AllDocumentedSymbols{$symbol} = 1;
3720                 } else {
3721                     # FIXME: print a warning?
3722                     #print ".. no description\n";
3723                 }
3724                 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3725             }
3727             my $pad1 = " " x (24 - length ($name));
3729             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";
3730             my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3731             $arg_desc .= MakeIndexterms($symbol, $id);
3732             $arg_desc .= "\n";
3733             $arg_desc .= OutputSymbolExtraLinks($symbol);
3735             $arg_desc .= "<programlisting>  “$name”$pad1 $type_output</programlisting>\n";
3736             $arg_desc .= $blurb;
3737             $arg_desc .= &MakeDeprecationNote($symbol);
3739             if ($flags_string) {
3740               $arg_desc  .= "<para>Flags: $flags_string</para>\n";
3741             }
3742             if ($range ne "") {
3743                 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3744             }
3745             if ($default ne "") {
3746                 $arg_desc .= "<para>Default value: $default_output</para>\n";
3747             }
3748             $arg_desc .= OutputSymbolTraits ($symbol);
3749             $arg_desc .= "</refsect2>\n";
3751             if ($flags =~ m/c/) {
3752                 $child_synop .= $arg_synop;
3753                 $child_desc .= $arg_desc;
3754             }
3755             elsif ($flags =~ m/s/) {
3756                 $style_synop .= $arg_synop;
3757                 $style_desc .= $arg_desc;
3758             }
3759             else {
3760                 $synop .= $arg_synop;
3761                 $desc .= $arg_desc;
3762             }
3763         }
3764     }
3765     return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3769 #############################################################################
3770 # Function    : ReadSourceDocumentation
3771 # Description : This reads in the documentation embedded in comment blocks
3772 #                in the source code (for Gnome).
3774 #                Parameter descriptions override any in the template files.
3775 #                Function descriptions are placed before any description from
3776 #                the template files.
3778 #                It recursively descends the source directory looking for .c
3779 #                files and scans them looking for specially-formatted comment
3780 #                blocks.
3782 # Arguments   : $source_dir - the directory to scan.
3783 #############m###############################################################
3785 sub ReadSourceDocumentation {
3786     my ($source_dir) = @_;
3787     my ($file, $dir, @suffix_list, $suffix);
3789     # prepend entries from @SOURCE_DIR
3790     for my $dir (@SOURCE_DIRS) {
3791         # Check if the filename is in the ignore list.
3792         if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3793             @TRACE@("Skipping source directory: $source_dir");
3794             return;
3795         }
3796     }
3798     @TRACE@("Scanning source directory: $source_dir");
3800     # This array holds any subdirectories found.
3801     my (@subdirs) = ();
3803     @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3805     opendir (SRCDIR, $source_dir)
3806         || die "Can't open source directory $source_dir: $!";
3808     foreach $file (readdir (SRCDIR)) {
3809       if ($file =~ /^\./) {
3810         next;
3811       } elsif (-d "$source_dir/$file") {
3812         push (@subdirs, $file);
3813       } elsif (@suffix_list) {
3814         foreach $suffix (@suffix_list) {
3815           if ($file =~ m/\.\Q${suffix}\E$/) {
3816             &ScanSourceFile ("$source_dir/$file");
3817           }
3818         }
3819       } elsif ($file =~ m/\.[ch]$/) {
3820         &ScanSourceFile ("$source_dir/$file");
3821       }
3822     }
3823     closedir (SRCDIR);
3825     # Now recursively scan the subdirectories.
3826     foreach $dir (@subdirs) {
3827         &ReadSourceDocumentation ("$source_dir/$dir");
3828     }
3832 #############################################################################
3833 # Function    : ScanSourceFile
3834 # Description : Scans one source file looking for specially-formatted comment
3835 #                blocks. Later &MergeSourceDocumentation is used to merge any
3836 #                documentation found with the documentation already read in
3837 #                from the template files.
3839 # Arguments   : $file - the file to scan.
3840 #############################################################################
3842 sub ScanSourceFile {
3843     my ($file) = @_;
3844     my $basename;
3846     # prepend entries from @SOURCE_DIR
3847     for my $dir (@SOURCE_DIRS) {
3848         # Check if the filename is in the ignore list.
3849         if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3850             @TRACE@("Skipping source file: $file");
3851             return;
3852         }
3853     }
3855     if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3856         $basename = $1;
3857     } else {
3858         &LogWarning ($file, 1, "Can't find basename for this filename.");
3859         $basename = $file;
3860     }
3862     # Check if the basename is in the list of files to ignore.
3863     if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3864         @TRACE@("Skipping source file: $file");
3865         return;
3866     }
3868     @TRACE@("Scanning source file: $file");
3870     open (SRCFILE, $file)
3871         || die "Can't open $file: $!";
3872     my $in_comment_block = 0;
3873     my $symbol;
3874     my $in_part = "";
3875     my ($description, $return_desc, $return_start, $return_style);
3876     my ($since_desc, $stability_desc, $deprecated_desc);
3877     my $current_param;
3878     my $ignore_broken_returns;
3879     my @params;
3880     while (<SRCFILE>) {
3881         # Look for the start of a comment block.
3882         if (!$in_comment_block) {
3883             if (m%^\s*/\*.*\*/%) {
3884                 #one-line comment - not gtkdoc
3885             } elsif (m%^\s*/\*\*\s%) {
3886                 #print "Found comment block start\n";
3888                 $in_comment_block = 1;
3890                 # Reset all the symbol data.
3891                 $symbol = "";
3892                 $in_part = "";
3893                 $description = "";
3894                 $return_desc = "";
3895                 $return_style = "";
3896                 $since_desc = "";
3897                 $deprecated_desc = "";
3898                 $stability_desc = "";
3899                 $current_param = -1;
3900                 $ignore_broken_returns = 0;
3901                 @params = ();
3902             }
3903             next;
3904         }
3906         # We're in a comment block. Check if we've found the end of it.
3907         if (m%^\s*\*+/%) {
3908             if (!$symbol) {
3909                 # maybe its not even meant to be a gtk-doc comment?
3910                 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3911             } else {
3912                 # Add the return value description onto the end of the params.
3913                 if ($return_desc) {
3914                     push (@params, "Returns");
3915                     push (@params, $return_desc);
3916                     if ($return_style eq 'broken') {
3917                         &LogWarning ($file, $., "Free-form return value description in $symbol. Use `Returns:' to avoid ambiguities.");
3918                     }
3919                 }
3920                 # Convert special SGML characters
3921                 $description = &ConvertSGMLChars ($symbol, $description);
3922                 my $k;
3923                 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3924                     $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3925                 }
3927                 # Handle Section docs
3928                 if ($symbol =~ m/SECTION:\s*(.*)/) {
3929                     my $real_symbol=$1;
3930                     my $key;
3932                     if (scalar %KnownSymbols) {
3933                         if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
3934                             &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
3935                         }
3936                     }
3938                     #print "SECTION DOCS found in source for : '$real_symbol'\n";
3939                     $ignore_broken_returns = 1;
3940                     for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3941                         #print "   '".$params[$k]."'\n";
3942                         $params[$k] = "\L$params[$k]";
3943                         undef $key;
3944                         if ($params[$k] eq "short_description") {
3945                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
3946                         } elsif ($params[$k] eq "see_also") {
3947                             $key = "$TMPL_DIR/$real_symbol:See_Also";
3948                         } elsif ($params[$k] eq "title") {
3949                             $key = "$TMPL_DIR/$real_symbol:Title";
3950                         } elsif ($params[$k] eq "stability") {
3951                             $key = "$TMPL_DIR/$real_symbol:Stability_Level";
3952                         } elsif ($params[$k] eq "section_id") {
3953                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
3954                         } elsif ($params[$k] eq "include") {
3955                             $key = "$TMPL_DIR/$real_symbol:Include";
3956                         } elsif ($params[$k] eq "image") {
3957                             $key = "$TMPL_DIR/$real_symbol:Image";
3958                         }
3959                         if (defined($key)) {
3960                             $SourceSymbolDocs{$key}=$params[$k+1];
3961                             $SourceSymbolSourceFile{$key} = $file;
3962                             $SourceSymbolSourceLine{$key} = $.;
3963                         }
3964                     }
3965                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
3966                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
3967                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
3968                     #$SourceSymbolTypes{$symbol} = "SECTION";
3969                 } else {
3970                     #print "SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n";
3971                     $SourceSymbolDocs{$symbol} = $description;
3972                     $SourceSymbolParams{$symbol} = [ @params ];
3973                     # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
3974                     #if (defined $DeclarationTypes{$symbol}) {
3975                     #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
3976                     #}
3977                     $SourceSymbolSourceFile{$symbol} = $file;
3978                     $SourceSymbolSourceLine{$symbol} = $.;
3979                 }
3981                 if ($since_desc) {
3982                      ($since_desc, my @extra_lines) = split ("\n", $since_desc);
3983                      $since_desc =~ s/^\s+//;
3984                      $since_desc =~ s/\s+$//;
3985                      #print "Since($symbol) : [$since_desc]\n";
3986                      $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
3987                      if(scalar @extra_lines) {
3988                          &LogWarning ($file, $., "multi-line since docs found");
3989                      }
3990                 }
3992                 if ($stability_desc) {
3993                     $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
3994                     $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
3995                 }
3997                 if ($deprecated_desc) {
3998                     if (!exists $Deprecated{$symbol}) {
3999                          # don't warn for signals and properties
4000                          #if ($symbol !~ m/::?(.*)/) {
4001                          if (defined $DeclarationTypes{$symbol}) {
4002                              &LogWarning ($file, $.,
4003                                  "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4004                                  " (See the --deprecated-guards option for gtkdoc-scan.)");
4005                          }
4006                     }
4007                     $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4008                 }
4009             }
4011             $in_comment_block = 0;
4012             next;
4013         }
4015         # Get rid of ' * ' at start of every line in the comment block.
4016         s%^\s*\*\s?%%;
4017         # But make sure we don't get rid of the newline at the end.
4018         if (!$_) {
4019             $_ = "\n";
4020         }
4021         #print "DEBUG: scanning :$_";
4023         # If we haven't found the symbol name yet, look for it.
4024         if (!$symbol) {
4025             if (m%^\s*(SECTION:\s*\S+)%) {
4026                 $symbol = $1;
4027                 #print "SECTION DOCS found in source for : '$symbol'\n";
4028                 $ignore_broken_returns = 1;
4029             } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-a-z0-9_ ]+\)\s*)*$%) {
4030                 $symbol = $1;
4031                 #print "SYMBOL DOCS found in source for : '$symbol'\n";
4032             }
4033             next;
4034         }
4036         if ($in_part eq "description") {
4037             # Get rid of 'Description:'
4038             s%^\s*Description:%%;
4039         }
4041         if (m/^\s*(returns:|return\s+value:)/i) {
4042             if ($return_style eq 'broken') {
4043                 $description .= $return_start . $return_desc;
4044             }
4045             $return_start = $1;
4046             if ($return_style eq 'sane') {
4047                 &LogWarning ($file, $., "Multiple Returns for $symbol.");
4048             }
4049             $return_style = 'sane';
4050             $ignore_broken_returns = 1;
4051             $return_desc = $';
4052             $in_part = "return";
4053             next;
4054         } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
4055             $return_start = $1;
4056             $return_style = 'broken';
4057             $return_desc = $';
4058             $in_part = "return";
4059             next;
4060         } elsif (m%^\s*since:%i) {
4061             # we're in param section and have not seen the blank line
4062             if($in_part ne "") {
4063               $since_desc = $';
4064               $in_part = "since";
4065               next;
4066             }
4067         } elsif (m%^\s*deprecated:%i) {
4068             # we're in param section and have not seen the blank line
4069             if($in_part ne "") {
4070               $deprecated_desc = $';
4071               $in_part = "deprecated";
4072               next;
4073             }
4074         } elsif (m%^\s*stability:%i) {
4075             $stability_desc = $';
4076             $in_part = "stability";
4077             next;
4078         }
4080         if ($in_part eq "description") {
4081             $description .= $_;
4082             next;
4083         } elsif ($in_part eq "return") {
4084             $return_desc .= $_;
4085             next;
4086         } elsif ($in_part eq "since") {
4087             $since_desc .= $_;
4088             next;
4089         } elsif ($in_part eq "stability") {
4090             $stability_desc .= $_;
4091             next;
4092         } elsif ($in_part eq "deprecated") {
4093             $deprecated_desc .= $_;
4094             next;
4095         }
4097         # We must be in the parameters. Check for the empty line below them.
4098         if (m%^\s*$%) {
4099             $in_part = "description";
4100             next;
4101         }
4103         # Look for a parameter name.
4104         if (m%^\s*@(\S+)\s*:\s*%) {
4105             my $param_name = $1;
4106             my $param_desc = $';
4108             #print "Found parameter: $param_name\n";
4109             # Allow varargs variations
4110             if ($param_name =~ m/^\.\.\.$/) {
4111                 $param_name = "...";
4112             }
4113             if ("\L$param_name" eq "returns") {
4114                 $return_style = 'sane';
4115                 $ignore_broken_returns = 1;
4116             }
4117             @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4119             push (@params, $param_name);
4120             push (@params, $param_desc);
4121             $current_param += $PARAM_FIELD_COUNT;
4122             next;
4123         }
4125         # We must be in the middle of a parameter description, so add it on
4126         # to the last element in @params.
4127         if ($current_param == -1) {
4128             &LogWarning ($file, $., "Parsing comment block file : parameter expected.");
4129         } else {
4130             $params[$#params] .= $_;
4131         }
4132     }
4133     close (SRCFILE);
4136 #############################################################################
4137 # Function    : OutputMissingDocumentation
4138 # Description : Outputs report of documentation coverage to a file
4140 # Arguments   : none
4141 #############################################################################
4143 sub OutputMissingDocumentation {
4144     my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4145     my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4147     my $n_documented = 0;
4148     my $n_incomplete = 0;
4149     my $total = 0;
4150     my $symbol;
4151     my $percent;
4152     my $msg;
4153     my $buffer = "";
4154     my $buffer_deprecated = "";
4155     my $buffer_descriptions = "";
4157     open(UNDOCUMENTED, ">$new_undocumented_file")
4158       || die "Can't create $new_undocumented_file";
4160     foreach $symbol (sort (keys (%AllSymbols))) {
4161         # FIXME: should we print LogWarnings for undocumented stuff?
4162         # DEBUG
4163         #my $ssfile = &GetSymbolSourceFile($symbol);
4164         #my $ssline = &GetSymbolSourceLine($symbol);
4165         #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4166         # DEBUG
4167         if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4168             $total++;
4169             if (exists ($AllDocumentedSymbols{$symbol})) {
4170                 $n_documented++;
4171                 if (exists ($AllIncompleteSymbols{$symbol})) {
4172                     $n_incomplete++;
4173                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4174                     #$buffer .= "\t0: ".$location;
4175                 }
4176             } elsif (exists $Deprecated{$symbol}) {
4177                 if (exists ($AllIncompleteSymbols{$symbol})) {
4178                     $n_incomplete++;
4179                     $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4180                     #$buffer .= "\t1a: ".$location;
4181                 } else {
4182                     $buffer_deprecated .= $symbol . "\n";
4183                     #$buffer .= "\t1b: ".$location;
4184                 }
4185             } else {
4186                 if (exists ($AllIncompleteSymbols{$symbol})) {
4187                     $n_incomplete++;
4188                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4189                     #$buffer .= "\t2a: ".$location;
4190                 } else {
4191                     $buffer .= $symbol . "\n";
4192                     #$buffer .= "\t2b: ".$location;
4193                 }
4194             }
4195         } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4196             $total++;
4197             #my $len1=(exists($SymbolDocs{$symbol}))?length($SymbolDocs{$symbol}):-1;
4198             #my $len2=(exists($AllDocumentedSymbols{$symbol}))?length($AllDocumentedSymbols{$symbol}):-1;
4199             #print "%%%% $symbol : $len1,$len2\n";
4200             if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4201             || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4202               $n_documented++;
4203             } else {
4204               # cut off the leading namespace ($TMPL_DIR)
4205               $symbol =~ m/^.*\/(.*)$/;
4206               $buffer_descriptions .= $1 . "\n";
4207             }
4208         }
4209     }
4211     $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
4213     if ($total == 0) {
4214       $percent = 100;
4215     } else {
4216       $percent = ($n_documented / $total) * 100.0;
4217     }
4219     printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4220     print UNDOCUMENTED "$n_documented symbols documented.\n";
4221     print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4222     print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
4224     print UNDOCUMENTED $buffer;
4225     close (UNDOCUMENTED);
4227     return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4229     printf "%.0f%% symbol docs coverage", $percent;
4230     print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4231     print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4235 #############################################################################
4236 # Function    : OutputUndeclaredSymbols
4237 # Description : Outputs symbols that are listed in the section file, but not
4238 #               declaration is found in the sources
4240 # Arguments   : none
4241 #############################################################################
4243 sub OutputUndeclaredSymbols {
4244     my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4245     my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4247     open(UNDECLARED, ">$new_undeclared_file")
4248         || die "Can't create $new_undeclared_file";
4250     if (%UndeclaredSymbols) {
4251         print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4252         print UNDECLARED "\n";
4253         print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4254     }
4255     close(UNDECLARED);
4257     return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4260 #############################################################################
4261 # Function    : OutputUnusedSymbols
4262 # Description : Outputs symbols that are documented in comments, but not
4263 #               declared in the sources
4265 # Arguments   : none
4266 #############################################################################
4268 sub OutputUnusedSymbols {
4269     my $num_unused = 0;
4270     my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4271     my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4273     open (UNUSED, ">$new_unused_file")
4274         || die "Can't open $new_unused_file";
4275     my ($symbol);
4276     foreach $symbol (sort keys (%Declarations)) {
4277         if (!defined ($DeclarationOutput{$symbol})) {
4278             print (UNUSED "$symbol\n");
4279             $num_unused++;
4280         }
4281     }
4282     foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4283         print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4284         $num_unused++;
4285     }
4286     close (UNUSED);
4287     if ($num_unused != 0) {
4288         &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4289             "They should be added to $MODULE-sections.txt in the appropriate place.");
4290     }
4292     return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4296 #############################################################################
4297 # Function    : OutputAllSymbols
4298 # Description : Outputs list of all symbols to a file
4300 # Arguments   : none
4301 #############################################################################
4303 sub OutputAllSymbols {
4304      my $n_documented = 0;
4305      my $total = 0;
4306      my $symbol;
4307      my $percent;
4308      my $msg;
4310      open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4311           || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4313      foreach $symbol (sort (keys (%AllSymbols))) {
4314           print SYMBOLS $symbol . "\n";
4315      }
4317      close (SYMBOLS);
4320 #############################################################################
4321 # Function    : OutputSymbolsWithoutSince
4322 # Description : Outputs list of all symbols without a since tag to a file
4324 # Arguments   : none
4325 #############################################################################
4327 sub OutputSymbolsWithoutSince {
4328      my $n_documented = 0;
4329      my $total = 0;
4330      my $symbol;
4331      my $percent;
4332      my $msg;
4334      open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4335           || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4337      foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4338          if (!defined $Since{$symbol}) {
4339              print SYMBOLS $symbol . "\n";
4340          }
4341      }
4343      close (SYMBOLS);
4347 #############################################################################
4348 # Function    : MergeSourceDocumentation
4349 # Description : This merges documentation read from a source file into the
4350 #                documentation read in from a template file.
4352 #                Parameter descriptions override any in the template files.
4353 #                Function descriptions are placed before any description from
4354 #                the template files.
4356 # Arguments   : none
4357 #############################################################################
4359 sub MergeSourceDocumentation {
4360     my $symbol;
4361     my @Symbols;
4363     if (scalar %SymbolDocs) {
4364         @Symbols=keys (%SymbolDocs);
4365         #print "num existing entries: ".(scalar @Symbols)."\n";
4366         #print "  ",$Symbols[0], " ",$Symbols[1],"\n";
4367     }
4368     else {
4369         # filter scanned declarations, with what we suppress from -sections.txt
4370         my %tmp = ();
4371         foreach $symbol (keys (%Declarations)) {
4372             if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4373                 $tmp{$symbol}=1;
4374             }
4375         }
4376         # , add the rest from -sections.txt
4377         foreach $symbol (keys (%KnownSymbols)) {
4378             if ($KnownSymbols{$symbol} == 1) {
4379                 $tmp{$symbol}=1;
4380             }
4381         }
4382         # and add whats found in the source
4383         foreach $symbol (keys (%SourceSymbolDocs)) {
4384             $tmp{$symbol}=1;
4385         }
4386         @Symbols = keys (%tmp);
4387         #print "num source entries: ".(scalar @Symbols)."\n";
4388     }
4389     foreach $symbol (@Symbols) {
4390         $AllSymbols{$symbol} = 1;
4392         my $have_tmpl_docs = 0;
4394         ## see if the symbol is documented in template
4395         my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4396         my $check_tmpl_doc =$tmpl_doc;
4397         # remove all xml-tags and whitespaces
4398         $check_tmpl_doc =~ s/<.*?>//g;
4399         $check_tmpl_doc =~ s/\s//g;
4400         # anything left ?
4401         if ($check_tmpl_doc ne "") {
4402             $have_tmpl_docs = 1;
4403             #print "## [$check_tmpl_doc]\n";
4404         } else {
4405             # if the docs have just an empty para, don't merge that.
4406             $check_tmpl_doc = $tmpl_doc;
4407             $check_tmpl_doc =~ s/(\s|\n)//msg;
4408             if ($check_tmpl_doc eq "<para></para>") {
4409                $tmpl_doc = "";
4410             }
4411         }
4413         if (exists ($SourceSymbolDocs{$symbol})) {
4414             my $type = $DeclarationTypes {$symbol};
4416             #print "merging [$symbol] from source\n";
4418             my $item = "Parameter";
4419             if (defined ($type)) {
4420                 if ($type eq 'STRUCT') {
4421                     $item = "Field";
4422                 } elsif ($type eq 'ENUM') {
4423                     $item = "Value";
4424                 } elsif ($type eq 'UNION') {
4425                     $item = "Field";
4426                 }
4427             } else {
4428                 $type="SIGNAL";
4429             }
4431             my $src_doc = $SourceSymbolDocs{$symbol};
4432             # remove leading and training whitespaces
4433             $src_doc =~ s/^\s+//;
4434             $src_doc =~ s/\s+$//;
4436             # Don't output warnings for overridden titles as titles are
4437             # automatically generated in the -sections.txt file, and thus they
4438             # are often overridden.
4439             if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4440                 # check if content is different
4441                 if ($tmpl_doc ne $src_doc) {
4442                     #print "[$tmpl_doc] [$src_doc]\n";
4443                     &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4444                         "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4445                 }
4446             }
4448             if ($src_doc ne "") {
4449                  $AllDocumentedSymbols{$symbol} = 1;
4450             }
4452             # Convert <!--PARAMETERS--> with any blank lines around it to
4453             # a </para> followed by <!--PARAMETERS--> followed by <para>.
4454             $src_doc =~ s%\n+\s*<!--PARAMETERS-->\s*\n+%\n</para>\n<!--PARAMETERS-->\n<para>\n%g;
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                     #print "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                     #print "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                         #print "  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                             #print "    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                                 #print "  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                 #print "merging [$symbol] from template\n";
4554             }
4555             else {
4556                 #print "[$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                 #print "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    #print "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
4893     if ($line =~ /^[ ]*<!DOCTYPE/) {
4894       push @md_blocks, $md_block;
4896       $md_block = { type   => "markup",
4897                     text   => $deindented_line,
4898                     start  => "<",
4899                     end    => ">",
4900                     closed => 0,
4901                     depth  => 0 };
4903     } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4904       # markup, including <?xml version="1.0"?>
4905       my $tag = $1;
4906       my $is_self_closing = defined($2);
4907       # FIXME: why do we need to skip https? here, if we generalize this to all
4908       # uri schemes we get parsing errors
4909       if (! $MD_TEXT_LEVEL_ELEMENTS{$tag} && $tag !~ /^https?/) {
4910         push @md_blocks, $md_block;
4912         if ($is_self_closing) {
4913           $md_block = { type => "self-closing tag",
4914                         text => $deindented_line };
4915           $is_self_closing = 0;
4916           next OUTER;
4917         }
4919         $md_block = { type   => "markup",
4920                       text   => $deindented_line,
4921                       start  => "<" . $tag . ">",
4922                       end    => "</" . $tag . ">",
4923                       closed => 0,
4924                       depth  => 0 };
4925         if ($deindented_line =~ /<\/$tag>/) {
4926           $md_block->{"closed"} = 1;
4927         }
4928         next OUTER;
4929       }
4930     } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
4931       # li
4932       push @md_blocks, $md_block;
4933       my $lines = $2;
4934       my $indentation = $1;
4935       $lines =~ s/^[ ]{0,4}//;
4936       $md_block = { type => "li",
4937                     ordered => 0,
4938                     indentation => $indentation,
4939                     marker => "[*+-]",
4940                     first => 1,
4941                     last => 1,
4942                     lines => [ $lines ] };
4943       next OUTER;
4944     } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
4945       push @md_blocks, $md_block;
4946       $md_block = { type => "quote",
4947                     lines => [ $1 ] };
4948       next OUTER;
4949     }
4951     # list item
4953     if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
4954       push @md_blocks, $md_block;
4955       my $lines = $2;
4956       my $indentation = $1;
4957       $lines =~ s/^[ ]{0,4}//;
4959       $md_block = { type => "li",
4960                     ordered => 1,
4961                     indentation => $indentation,
4962                     marker => "\\d+[.]",
4963                     first => 1,
4964                     last => 1,
4965                     lines => [ $lines ] };
4967       next;
4968     }
4970     # paragraph
4971     if ($md_block->{"type"} eq "paragraph") {
4972       if ($md_block->{"interrupted"}) {
4973         push @md_blocks, $md_block;
4974         $md_block = { type => "paragraph",
4975                       interrupted => 0,
4976                       text => $line };
4977       } else {
4978         $md_block->{"text"} .= "\n" . $line;
4979       }
4980     } else {
4981       push @md_blocks, $md_block;
4982       $md_block = { type => "paragraph",
4983                     text => $line };
4984     }
4985   }
4987   push @md_blocks, $md_block;
4989   shift @md_blocks;
4991   return @md_blocks;
4994 sub MarkDownParseSpanElementsInner {
4995   my ($text, $markersref) = @_;
4996   my $markup = "";
4997   my %markers = map { $_ => 1 } @$markersref;
4999   while ($text ne "") {
5000     my $closest_marker = "";
5001     my $closest_marker_index = 0;
5002     my $closest_marker_position = -1;
5003     my $text_marker = "";
5004     my $i = 0;
5005     my $offset = 0;
5006     my @markers_rest;
5007     my $marker;
5008     my $use;
5010     while ( ($marker, $use) = each %markers ) {
5011       my $marker_position;
5013       if (!$use) {
5014         next;
5015       }
5017       $marker_position = index ($text, $marker);
5019       if ($marker_position < 0) {
5020         $markers{$marker} = 0;
5021         next;
5022       }
5024       if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5025         $closest_marker = $marker;
5026         $closest_marker_index = $i;
5027         $closest_marker_position = $marker_position;
5028       }
5029     }
5031     if ($closest_marker_position >= 0) {
5032       $text_marker = substr ($text, $closest_marker_position);
5033     }
5035     if ($text_marker eq "") {
5036       $markup .= $text;
5037       $text = "";
5038       next; # last
5039     }
5041     $markup .= substr ($text, 0, $closest_marker_position);
5042     $text = substr ($text, $closest_marker_position);
5043     @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5045     if ($closest_marker eq "![" || $closest_marker eq "[") {
5046       my %element;
5048       if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5049         my $remaining_text;
5051         %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5052                      "a" => $1 );
5054         $offset = length ($&);
5055         if ($element{"!"}) {
5056           $offset++;
5057         }
5059         $remaining_text = substr ($text, $offset);
5060         if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5061           $element{"»"} = $1;
5062           if (defined ($2)) {
5063             $element{"#"} = $2;
5064           }
5065           $offset += length ($&);
5066         } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5067           $element{"ref"} = $1;
5068           $offset += length ($&);
5069         } else {
5070           undef %element;
5071         }
5072       }
5074       if (%element) {
5075         if ($element{"»"}) {
5076           $element{"»"} =~ s/&/&amp;/g;
5077           $element{"»"} =~ s/</&lt;/g;
5078         }
5079         if ($element{"!"}) {
5080           $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5082           if (defined ($element{"a"})) {
5083             $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5084           }
5086           $markup .= "</inlinemediaobject>";
5087         } elsif ($element{"ref"}) {
5088           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5089           $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5091           if (defined ($element{"#"})) {
5092             # title attribute not supported
5093           }
5095           $markup .= ">" . $element{"a"} . "</link>";
5096         } else {
5097           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5098           $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5100           if (defined ($element{"#"})) {
5101             # title attribute not supported
5102           }
5104           $markup .= ">" . $element{"a"} . "</ulink>";
5105         }
5106       } else {
5107         $markup .= $closest_marker;
5108         if ($closest_marker eq "![") {
5109           $offset = 2;
5110         } else {
5111           $offset = 1;
5112         }
5113       }
5114     } elsif ($closest_marker eq "<") {
5115       if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5116         my $element_url = $1;
5117         $element_url =~ s/&/&amp;/g;
5118         $element_url =~ s/</&lt;/g;
5120         $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5121         $offset = length ($&);
5122       } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5123         $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5124         $offset = length ($&);
5125       } elsif ($text =~ /^<[^>]+?>/) {
5126         $markup .= $&;
5127         $offset = length ($&);
5128       } else {
5129         $markup .= "&lt;";
5130         $offset = 1;
5131       }
5132     } elsif ($closest_marker eq "\\") {
5133       my $special_char = substr ($text, 1, 1);
5134       if ($MD_ESCAPABLE_CHARS{$special_char} ||
5135           $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5136         $markup .= $special_char;
5137         $offset = 2;
5138       } else {
5139         $markup .= "\\";
5140         $offset = 1;
5141       }
5142     } elsif ($closest_marker eq "`") {
5143       if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5144         my $element_text = $2;
5145         $markup .= "<literal>" . $element_text . "</literal>";
5146         $offset = length ($&);
5147       } else {
5148         $markup .= "`";
5149         $offset = 1;
5150       }
5151     } elsif ($closest_marker eq "@") {
5152       # Convert '@param()'
5153       # FIXME: we could make those also links ($symbol.$2), but that would be less
5154       # useful as the link target is a few lines up or down
5155       if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5156         $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5157         $offset = length ($&);
5158       } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5159         # Convert '@param', but not '\@param'.
5160         $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5161         $offset = length ($&);
5162       } elsif ($text =~ /^\\\@/) {
5163         $markup .= "\@";
5164         $offset = length ($&);
5165       } else {
5166         $markup .= "@";
5167         $offset = 1;
5168       }
5169     } elsif ($closest_marker eq "#") {
5170       if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5171         # handle #Object.func()
5172         $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5173         $offset = length ($&);
5174       } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5175         # Convert '#symbol', but not '\#symbol'.
5176         $markup .= $1 . &MakeHashXRef ($2, "type");
5177         $offset = length ($&);
5178       } elsif ($text =~ /^\\#/) {
5179         $markup .= "#";
5180         $offset = length ($&);
5181       } else {
5182         $markup .= "#";
5183         $offset = 1;
5184       }
5185     } elsif ($closest_marker eq "%") {
5186       if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5187         # Convert '%constant', but not '\%constant'.
5188         # Also allow negative numbers, e.g. %-1.
5189         $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5190         $offset = length ($&);
5191       } elsif ($text =~ /^\\%/) {
5192         $markup .= "\%";
5193         $offset = length ($&);
5194       } else {
5195         $markup .= "%";
5196         $offset = 1;
5197       }
5198     }
5200     if ($offset > 0) {
5201       $text = substr ($text, $offset);
5202     }
5203   }
5205   return $markup;
5208 sub MarkDownParseSpanElements {
5209   my ($text) = @_;
5210   my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5212   $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5214   # Convert 'function()' or 'macro()'.
5215   # if there is abc_*_def() we don't want to make a link to _def()
5216   # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5217   $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5219   return $text;
5222 sub ReplaceEntities {
5223   my ($text, $symbol) = @_;
5224   my $warn = "";
5225   my @entities = ( [ "&lt;", "<" ],
5226                    [ "&gt;", ">" ],
5227                    [ "&ast;", "*" ],
5228                    [ "&num;", "#" ],
5229                    [ "&percnt;", "%"],
5230                    [ "&colon;", ":" ],
5231                    [ "&quot;", "\"" ],
5232                    [ "&apos;", "'" ],
5233                    [ "&nbsp;", " " ],
5234                    [ "&amp;", "&" ] ); # Do this last, or the others get messed up.
5235   my $i;
5237   # Expand entities in <programlisting> even inside CDATA since
5238   # we changed the definition of |[ to add CDATA
5239   for ($i = 0; $i <= $#entities; $i++) {
5240     if ($text =~ s/$entities[$i][0]/$entities[$i][1]/g) {
5241       # don't warn about &ast; since it is expected to be present
5242       # for C-style comments
5243       if ($entities[$i][0] ne "&ast;") {
5244         $warn .= "$entities[$i][0] ";
5245       }
5246     }
5247   }
5249   if ($warn ne "") {
5250     chomp $warn;
5251     &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
5252                  "Deprecated entities found in documentation for $symbol: $warn");
5253   }
5255   return $text;
5258 sub MarkDownOutputDocBook {
5259   my ($blocksref, $symbol, $context) = @_;
5260   my $output = "";
5261   my $block;
5262   my @blocks = @$blocksref;
5264   foreach $block (@blocks) {
5265     my $text;
5266     my $title;
5268     if ($block->{"type"} eq "paragraph") {
5269       $text = &MarkDownParseSpanElements ($block->{"text"});
5270       if ($context eq "li" && $output eq "") {
5271         if ($block->{"interrupted"}) {
5272           $output .= "\n"."<para>".$text."</para>"."\n";
5273         } else {
5274           $output .= "<para>".$text."</para>";
5275           if ($#blocks > 0) {
5276             $output .= "\n";
5277           }
5278         }
5279       } else {
5280         $output .= "<para>".$text."</para>"."\n";
5281       }
5283     } elsif ($block->{"type"} eq "heading") {
5284       my $tag;
5286       $title = &MarkDownParseSpanElements ($block->{"text"});
5288       if ($block->{"level"} == 1) {
5289         $tag = "refsect2";
5290       } else {
5291         $tag = "refsect3";
5292       }
5294       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5295       if (defined ($block->{"id"})) {
5296         $output .= "<" . $tag . " id=\"" . $block->{"id"} . "\">";
5297       } else {
5298         $output .= "<" . $tag . ">";
5299       }
5301       $output .= "<title>" . $title . "</title>" . $text . "</" . $tag . ">\n";
5302     } elsif ($block->{"type"} eq "li") {
5303       my $tag = "itemizedlist";
5305       if ($block->{"first"}) {
5306         if ($block->{"ordered"}) {
5307           $tag = "orderedlist";
5308         }
5309         $output .= "<".$tag.">\n";
5310       }
5312       if ($block->{"interrupted"}) {
5313         push $block->{"lines"}, "";
5314       }
5316       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5317       $output .= "<listitem>".$text."</listitem>\n";
5318       if ($block->{"last"}) {
5319         if ($block->{"ordered"}) {
5320           $tag = "orderedlist";
5321         }
5322         $output .= "</".$tag.">\n";
5323       }
5324     } elsif ($block->{"type"} eq "quote") {
5325       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5326       $output .= "<blockquote>\n" . $text . "</blockquote>\n";
5327     } elsif ($block->{"type"} eq "code") {
5328       if ($block->{"language"}) {
5329         $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5330       } else {
5331         $output .= "<informalexample><programlisting><![CDATA[\n";
5332       }
5333       foreach (@{$block->{"lines"}}) {
5334         $output .= &ReplaceEntities ($_, $symbol) . "\n";
5335       }
5336       $output .= "]]></programlisting></informalexample>\n";
5337     } elsif ($block->{"type"} eq "markup") {
5338       $text = &ExpandAbbreviations($symbol, $block->{"text"});
5339       $output .= $text."\n";
5340     } else {
5341       $output .= $block->{"text"}."\n";
5342     }
5343   }
5345   return $output;
5348 sub MarkDownParseLines {
5349   my ($linesref, $symbol, $context) = @_;
5350   my $output;
5351   my @lines = @$linesref;
5352   my @blocks;
5354   @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5355   $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5357   return $output;
5360 sub MarkDownParse {
5361   my ($text, $symbol) = @_;
5362   my @lines;
5364   # take out some variability in line endings
5365   $text =~ s%\r\n%\n%g;
5366   $text =~ s%\r%\n%g;
5368   # split lines
5369   @lines = split("\n", $text);
5370   $text = MarkDownParseLines(\@lines, $symbol, "");
5372   return $text;
5375 #############################################################################
5376 # LIBRARY FUNCTIONS -        These functions are used in both gtkdoc-mkdb and
5377 #                        gtkdoc-mktmpl and should eventually be moved to a
5378 #                        separate library.
5379 #############################################################################
5381 #############################################################################
5382 # Function    : ReadDeclarationsFile
5383 # Description : This reads in a file containing the function/macro/enum etc.
5384 #                declarations.
5386 #                Note that in some cases there are several declarations with
5387 #                the same name, e.g. for conditional macros. In this case we
5388 #                set a flag in the %DeclarationConditional hash so the
5389 #                declaration is not shown in the docs.
5391 #                If a macro and a function have the same name, e.g. for
5392 #                gtk_object_ref, the function declaration takes precedence.
5394 #                Some opaque structs are just declared with 'typedef struct
5395 #                _name name;' in which case the declaration may be empty.
5396 #                The structure may have been found later in the header, so
5397 #                that overrides the empty declaration.
5399 # Arguments   : $file - the declarations file to read
5400 #                $override - if declarations in this file should override
5401 #                        any current declaration.
5402 #############################################################################
5404 sub ReadDeclarationsFile {
5405     my ($file, $override) = @_;
5407     if ($override == 0) {
5408         %Declarations = ();
5409         %DeclarationTypes = ();
5410         %DeclarationConditional = ();
5411         %DeclarationOutput = ();
5412     }
5414     open (INPUT, $file)
5415         || die "Can't open $file: $!";
5416     my $declaration_type = "";
5417     my $declaration_name;
5418     my $declaration;
5419     my $is_deprecated = 0;
5420     while (<INPUT>) {
5421         if (!$declaration_type) {
5422             if (m/^<([^>]+)>/) {
5423                 $declaration_type = $1;
5424                 $declaration_name = "";
5425                 #print "Found declaration: $declaration_type\n";
5426                 $declaration = "";
5427             }
5428         } else {
5429             if (m%^<NAME>(.*)</NAME>%) {
5430                 $declaration_name = $1;
5431             } elsif (m%^<DEPRECATED/>%) {
5432                 $is_deprecated = 1;
5433             } elsif (m%^</$declaration_type>%) {
5434                 #print "Found end of declaration: $declaration_name\n";
5435                 # Check that the declaration has a name
5436                 if ($declaration_name eq "") {
5437                     print "ERROR: $declaration_type has no name $file:$.\n";
5438                 }
5440                 # If the declaration is an empty typedef struct _XXX XXX
5441                 # set the flag to indicate the struct has a typedef.
5442                 if ($declaration_type eq 'STRUCT'
5443                     && $declaration =~ m/^\s*$/) {
5444                     #print "Struct has typedef: $declaration_name\n";
5445                     $StructHasTypedef{$declaration_name} = 1;
5446                 }
5448                 # Check if the symbol is already defined.
5449                 if (defined ($Declarations{$declaration_name})
5450                     && $override == 0) {
5451                     # Function declarations take precedence.
5452                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5453                         # Ignore it.
5454                     } elsif ($declaration_type eq 'FUNCTION') {
5455                         if ($is_deprecated) {
5456                             $Deprecated{$declaration_name} = "";
5457                         }
5458                         $Declarations{$declaration_name} = $declaration;
5459                         $DeclarationTypes{$declaration_name} = $declaration_type;
5460                     } elsif ($DeclarationTypes{$declaration_name}
5461                               eq $declaration_type) {
5462                         # If the existing declaration is empty, or is just a
5463                         # forward declaration of a struct, override it.
5464                         if ($declaration_type eq 'STRUCT') {
5465                             if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5466                                 if ($is_deprecated) {
5467                                     $Deprecated{$declaration_name} = "";
5468                                 }
5469                                 $Declarations{$declaration_name} = $declaration;
5470                             } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5471                                 # Ignore an empty or forward declaration.
5472                             } else {
5473                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5474                             }
5475                         } else {
5476                             # set flag in %DeclarationConditional hash for
5477                             # multiply defined macros/typedefs.
5478                             $DeclarationConditional{$declaration_name} = 1;
5479                         }
5480                     } else {
5481                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5482                     }
5483                 } else {
5484                     if ($is_deprecated) {
5485                         $Deprecated{$declaration_name} = "";
5486                     }
5487                     $Declarations{$declaration_name} = $declaration;
5488                     $DeclarationTypes{$declaration_name} = $declaration_type;
5489                 }
5491                 $declaration_type = "";
5492                 $is_deprecated = 0;
5493             } else {
5494                 $declaration .= $_;
5495             }
5496         }
5497     }
5498     close (INPUT);
5502 #############################################################################
5503 # Function    : ReadSignalsFile
5504 # Description : This reads in an existing file which contains information on
5505 #                all GTK signals. It creates the arrays @SignalNames and
5506 #                @SignalPrototypes containing info on the signals. The first
5507 #                line of the SignalPrototype is the return type of the signal
5508 #                handler. The remaining lines are the parameters passed to it.
5509 #                The last parameter, "gpointer user_data" is always the same
5510 #                so is not included.
5511 # Arguments   : $file - the file containing the signal handler prototype
5512 #                        information.
5513 #############################################################################
5515 sub ReadSignalsFile {
5516     my ($file) = @_;
5518     my $in_signal = 0;
5519     my $signal_object;
5520     my $signal_name;
5521     my $signal_returns;
5522     my $signal_flags;
5523     my $signal_prototype;
5525     # Reset the signal info.
5526     @SignalObjects = ();
5527     @SignalNames = ();
5528     @SignalReturns = ();
5529     @SignalFlags = ();
5530     @SignalPrototypes = ();
5532     if (! -f $file) {
5533         return;
5534     }
5535     if (!open (INPUT, $file)) {
5536         warn "Can't open $file - skipping signals\n";
5537         return;
5538     }
5539     while (<INPUT>) {
5540         if (!$in_signal) {
5541             if (m/^<SIGNAL>/) {
5542                 $in_signal = 1;
5543                 $signal_object = "";
5544                 $signal_name = "";
5545                 $signal_returns = "";
5546                 $signal_prototype = "";
5547             }
5548         } else {
5549             if (m/^<NAME>(.*)<\/NAME>/) {
5550                 $signal_name = $1;
5551                 if ($signal_name =~ m/^(.*)::(.*)$/) {
5552                     $signal_object = $1;
5553                     ($signal_name = $2) =~ s/_/-/g;
5554                     #print "Found signal: $signal_name\n";
5555                 } else {
5556                     &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5557                 }
5558             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5559                 $signal_returns = $1;
5560             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5561                 $signal_flags = $1;
5562             } elsif (m%^</SIGNAL>%) {
5563                 #print "Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}";
5564                 push (@SignalObjects, $signal_object);
5565                 push (@SignalNames, $signal_name);
5566                 push (@SignalReturns, $signal_returns);
5567                 push (@SignalFlags, $signal_flags);
5568                 push (@SignalPrototypes, $signal_prototype);
5569                 $in_signal = 0;
5570             } else {
5571                 $signal_prototype .= $_;
5572             }
5573         }
5574     }
5575     close (INPUT);
5579 #############################################################################
5580 # Function    : ReadTemplateFile
5581 # Description : This reads in the manually-edited documentation file
5582 #               corresponding to the file currently being created, so we can
5583 #               insert the documentation at the appropriate places.
5584 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5585 #               is a hash of arrays.
5586 #               NOTE: This function is duplicated in gtkdoc-mktmpl (but
5587 #               slightly different).
5588 # Arguments   : $docsfile - the template file to read in.
5589 #               $skip_unused_params - 1 if the unused parameters should be
5590 #                 skipped.
5591 #############################################################################
5593 sub ReadTemplateFile {
5594     my ($docsfile, $skip_unused_params) = @_;
5596     my $template = "$docsfile.sgml";
5597     if (! -f $template) {
5598         #print "File doesn't exist: $template\n";
5599         return 0;
5600     }
5602     # start with empty hashes, we merge the source comment for each file
5603     # afterwards
5604     %SymbolDocs = ();
5605     %SymbolTypes = ();
5606     %SymbolParams = ();
5608     my $current_type = "";        # Type of symbol being read.
5609     my $current_symbol = "";        # Name of symbol being read.
5610     my $symbol_doc = "";                # Description of symbol being read.
5611     my @params;                        # Parameter names and descriptions of current
5612                                 #   function/macro/function typedef.
5613     my $current_param = -1;        # Index of parameter currently being read.
5614                                 #   Note that the param array contains pairs
5615                                 #   of param name & description.
5616     my $in_unused_params = 0;        # True if we are reading in the unused params.
5617     my $in_deprecated = 0;
5618     my $in_since = 0;
5619     my $in_stability = 0;
5621     open (DOCS, "$template")
5622         || die "Can't open $template: $!";
5624     @TRACE@("reading template $template");
5626     while (<DOCS>) {
5627         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5628             my $type = $1;
5629             my $symbol = $2;
5630             if ($symbol eq "Title"
5631                 || $symbol eq "Short_Description"
5632                 || $symbol eq "Long_Description"
5633                 || $symbol eq "See_Also"
5634                 || $symbol eq "Stability_Level"
5635                 || $symbol eq "Include"
5636                 || $symbol eq "Image") {
5638                 $symbol = $docsfile . ":" . $symbol;
5639             }
5641             #print "Found symbol: $symbol\n";
5642             # Remember file and line for the symbol
5643             $SymbolSourceFile{$symbol} = $template;
5644             $SymbolSourceLine{$symbol} = $.;
5646             # Store previous symbol, but remove any trailing blank lines.
5647             if ($current_symbol ne "") {
5648                 $symbol_doc =~ s/\s+$//;
5649                 $SymbolTypes{$current_symbol} = $current_type;
5650                 $SymbolDocs{$current_symbol} = $symbol_doc;
5652                 # Check that the stability level is valid.
5653                 if ($StabilityLevel{$current_symbol}) {
5654                     $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5655                 }
5657                 if ($current_param >= 0) {
5658                     $SymbolParams{$current_symbol} = [ @params ];
5659                 } else {
5660                     # Delete any existing params in case we are overriding a
5661                     # previously read template.
5662                     delete $SymbolParams{$current_symbol};
5663                 }
5664             }
5665             $current_type = $type;
5666             $current_symbol = $symbol;
5667             $current_param = -1;
5668             $in_unused_params = 0;
5669             $in_deprecated = 0;
5670             $in_since = 0;
5671             $in_stability = 0;
5672             $symbol_doc = "";
5673             @params = ();
5675         } elsif (m/^<!-- # Unused Parameters # -->/) {
5676             #print "DEBUG: Found unused parameters\n";
5677             $in_unused_params = 1;
5678             next;
5680         } elsif ($in_unused_params && $skip_unused_params) {
5681             # When outputting the DocBook we skip unused parameters.
5682             #print "DEBUG: Skipping unused param: $_";
5683             next;
5685         } else {
5686             # Check if param found. Need to handle "..." and "format...".
5687             if (s/^\@([\w\.]+):\040?//) {
5688                 my $param_name = $1;
5689                 my $param_desc = $_;
5690                 # Allow variations of 'Returns'
5691                 if ($param_name =~ m/^[Rr]eturns?$/) {
5692                     $param_name = "Returns";
5693                 }
5694                 # Allow varargs variations
5695                 if ($param_name =~ m/^.*\.\.\.$/) {
5696                     $param_name = "...";
5697                 }
5699                 # strip trailing whitespaces and blank lines
5700                 s/\s+\n$/\n/m;
5701                 s/\n+$/\n/sm;
5702                 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5704                 if ($param_name eq "Deprecated") {
5705                     $in_deprecated = 1;
5706                     $Deprecated{$current_symbol} = $_;
5707                 } elsif ($param_name eq "Since") {
5708                     $in_since = 1;
5709                     chomp;
5710                     $Since{$current_symbol} = $_;
5711                 } elsif ($param_name eq "Stability") {
5712                     $in_stability = 1;
5713                     $StabilityLevel{$current_symbol} = $_;
5714                 } else {
5715                     push (@params, $param_name);
5716                     push (@params, $param_desc);
5717                     $current_param += $PARAM_FIELD_COUNT;
5718                 }
5719             } else {
5720                 # strip trailing whitespaces and blank lines
5721                 s/\s+\n$/\n/m;
5722                 s/\n+$/\n/sm;
5724                 if (!m/^\s+$/) {
5725                     if ($in_deprecated) {
5726                         $Deprecated{$current_symbol} .= $_;
5727                     } elsif ($in_since) {
5728                         &LogWarning ($template, $., "multi-line since docs found");
5729                         #$Since{$current_symbol} .= $_;
5730                     } elsif ($in_stability) {
5731                         $StabilityLevel{$current_symbol} .= $_;
5732                     } elsif ($current_param >= 0) {
5733                         $params[$current_param] .= $_;
5734                     } else {
5735                         $symbol_doc .= $_;
5736                     }
5737                 }
5738             }
5739         }
5740     }
5742     # Remember to finish the current symbol doccs.
5743     if ($current_symbol ne "") {
5745         $symbol_doc =~ s/\s+$//;
5746         $SymbolTypes{$current_symbol} = $current_type;
5747         $SymbolDocs{$current_symbol} = $symbol_doc;
5749         # Check that the stability level is valid.
5750         if ($StabilityLevel{$current_symbol}) {
5751             $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5752         }
5754         if ($current_param >= 0) {
5755             $SymbolParams{$current_symbol} = [ @params ];
5756         } else {
5757             # Delete any existing params in case we are overriding a
5758             # previously read template.
5759             delete $SymbolParams{$current_symbol};
5760         }
5761     }
5763     close (DOCS);
5764     return 1;
5768 #############################################################################
5769 # Function    : ReadObjectHierarchy
5770 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5771 #                the GtkObject subclasses described in this module (and their
5772 #                ancestors).
5773 #                It places them in the @Objects array, and places their level
5774 #                in the object hierarchy in the @ObjectLevels array, at the
5775 #                same index. GtkObject, the root object, has a level of 1.
5777 #               FIXME: the version in gtkdoc-mkdb also generates tree_index.sgml
5778 #               as it goes along, this should be split out into a separate
5779 #               function.
5781 # Arguments   : none
5782 #############################################################################
5784 sub ReadObjectHierarchy {
5785     @Objects = ();
5786     @ObjectLevels = ();
5788     if (! -f $OBJECT_TREE_FILE) {
5789         return;
5790     }
5791     if (!open (INPUT, $OBJECT_TREE_FILE)) {
5792         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5793         return;
5794     }
5796     # FIXME: use $OUTPUT_FORMAT
5797     # my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.$OUTPUT_FORMAT";
5798     my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml";
5799     my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new";
5801     open (OUTPUT, ">$new_tree_index")
5802         || die "Can't create $new_tree_index: $!";
5804     if ($OUTPUT_FORMAT eq "xml") {
5805         my $tree_header = $doctype_header;
5807         $tree_header =~ s/<!DOCTYPE \w+/<!DOCTYPE screen/;
5808         print (OUTPUT "$tree_header");
5809     }
5810     print (OUTPUT "<screen>\n");
5812     # Only emit objects if they are supposed to be documented, or if
5813     # they have documented children. To implement this, we maintain a
5814     # stack of pending objects which will be emitted if a documented
5815     # child turns up.
5816     my @pending_objects = ();
5817     my @pending_levels = ();
5818     my $root;
5819     while (<INPUT>) {
5820         if (m/\S+/) {
5821             my $object = $&;
5822             my $level = (length($`)) / 2 + 1;
5823             my $xref = "";
5825             if ($level == 1) {
5826                 $root = $object;
5827             }
5829             while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5830                 my $pobject = pop(@pending_objects);
5831                 my $plevel = pop(@pending_levels);
5832             }
5834             push (@pending_objects, $object);
5835             push (@pending_levels, $level);
5837             if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5838                 while ($#pending_levels >= 0) {
5839                     $object = shift @pending_objects;
5840                     $level = shift @pending_levels;
5841                     $xref = &MakeXRef ($object);
5843                     print (OUTPUT ' ' x ($level * 4), "$xref\n");
5844                     push (@Objects, $object);
5845                     push (@ObjectLevels, $level);
5846                     $ObjectRoots{$object} = $root;
5847                 }
5848             }
5849             #else {
5850             #    LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5851             #}
5852         }
5853     }
5854     print (OUTPUT "</screen>\n");
5856     close (INPUT);
5857     close (OUTPUT);
5859     &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5861     &OutputObjectList;
5864 #############################################################################
5865 # Function    : ReadInterfaces
5866 # Description : This reads in the $MODULE.interfaces file.
5868 # Arguments   : none
5869 #############################################################################
5871 sub ReadInterfaces {
5872     %Interfaces = ();
5874     if (! -f $INTERFACES_FILE) {
5875         return;
5876     }
5877     if (!open (INPUT, $INTERFACES_FILE)) {
5878         warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5879         return;
5880     }
5882     while (<INPUT>) {
5883        chomp;
5884        my ($object, @ifaces) = split;
5885        if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5886            my @knownIfaces = ();
5888            # filter out private interfaces, but leave foreign interfaces
5889            foreach my $iface (@ifaces) {
5890                if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5891                    push (@knownIfaces, $iface);
5892                }
5893              }
5895            $Interfaces{$object} = join(' ', @knownIfaces);
5896        }
5897     }
5898     close (INPUT);
5901 #############################################################################
5902 # Function    : ReadPrerequisites
5903 # Description : This reads in the $MODULE.prerequisites file.
5905 # Arguments   : none
5906 #############################################################################
5908 sub ReadPrerequisites {
5909     %Prerequisites = ();
5911     if (! -f $PREREQUISITES_FILE) {
5912         return;
5913     }
5914     if (!open (INPUT, $PREREQUISITES_FILE)) {
5915         warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
5916         return;
5917     }
5919     while (<INPUT>) {
5920        chomp;
5921        my ($iface, @prereqs) = split;
5922        if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
5923            my @knownPrereqs = ();
5925            # filter out private prerequisites, but leave foreign prerequisites
5926            foreach my $prereq (@prereqs) {
5927                if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
5928                   push (@knownPrereqs, $prereq);
5929                }
5930            }
5932            $Prerequisites{$iface} = join(' ', @knownPrereqs);
5933        }
5934     }
5935     close (INPUT);
5938 #############################################################################
5939 # Function    : ReadArgsFile
5940 # Description : This reads in an existing file which contains information on
5941 #                all GTK args. It creates the arrays @ArgObjects, @ArgNames,
5942 #                @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
5943 #               on the args.
5944 # Arguments   : $file - the file containing the arg information.
5945 #############################################################################
5947 sub ReadArgsFile {
5948     my ($file) = @_;
5950     my $in_arg = 0;
5951     my $arg_object;
5952     my $arg_name;
5953     my $arg_type;
5954     my $arg_flags;
5955     my $arg_nick;
5956     my $arg_blurb;
5957     my $arg_default;
5958     my $arg_range;
5960     # Reset the args info.
5961     @ArgObjects = ();
5962     @ArgNames = ();
5963     @ArgTypes = ();
5964     @ArgFlags = ();
5965     @ArgNicks = ();
5966     @ArgBlurbs = ();
5967     @ArgDefaults = ();
5968     @ArgRanges = ();
5970     if (! -f $file) {
5971         return;
5972     }
5973     if (!open (INPUT, $file)) {
5974         warn "Can't open $file - skipping args\n";
5975         return;
5976     }
5977     while (<INPUT>) {
5978         if (!$in_arg) {
5979             if (m/^<ARG>/) {
5980                 $in_arg = 1;
5981                 $arg_object = "";
5982                 $arg_name = "";
5983                 $arg_type = "";
5984                 $arg_flags = "";
5985                 $arg_nick = "";
5986                 $arg_blurb = "";
5987                 $arg_default = "";
5988                 $arg_range = "";
5989             }
5990         } else {
5991             if (m/^<NAME>(.*)<\/NAME>/) {
5992                 $arg_name = $1;
5993                 if ($arg_name =~ m/^(.*)::(.*)$/) {
5994                     $arg_object = $1;
5995                     ($arg_name = $2) =~ s/_/-/g;
5996                     #print "Found arg: $arg_name\n";
5997                 } else {
5998                     &LogWarning ($file, $., "Invalid argument name: $arg_name");
5999                 }
6000             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6001                 $arg_type = $1;
6002             } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6003                 $arg_range = $1;
6004             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6005                 $arg_flags = $1;
6006             } elsif (m/^<NICK>(.*)<\/NICK>/) {
6007                 $arg_nick = $1;
6008             } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6009                 $arg_blurb = $1;
6010                 if ($arg_blurb eq "(null)") {
6011                   $arg_blurb = "";
6012                   &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6013                 }
6014             } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6015                 $arg_default = $1;
6016             } elsif (m%^</ARG>%) {
6017                 #print "Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n";
6018                 push (@ArgObjects, $arg_object);
6019                 push (@ArgNames, $arg_name);
6020                 push (@ArgTypes, $arg_type);
6021                 push (@ArgRanges, $arg_range);
6022                 push (@ArgFlags, $arg_flags);
6023                 push (@ArgNicks, $arg_nick);
6024                 push (@ArgBlurbs, $arg_blurb);
6025                 push (@ArgDefaults, $arg_default);
6026                 $in_arg = 0;
6027             }
6028         }
6029     }
6030     close (INPUT);
6034 #############################################################################
6035 # Function    : CheckIsObject
6036 # Description : Returns 1 if the given name is a GObject or a subclass.
6037 #                It uses the global @Objects array.
6038 #                Note that the @Objects array only contains classes in the
6039 #                current module and their ancestors - not all GObject classes.
6040 # Arguments   : $name - the name to check.
6041 #############################################################################
6043 sub CheckIsObject {
6044     my ($name) = @_;
6045     my $root = $ObjectRoots{$name};
6046     # Let GBoxed pass as an object here to get -struct appended to the id
6047     # and prevent conflicts with sections.
6048     return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6052 #############################################################################
6053 # Function    : MakeReturnField
6054 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6055 # Arguments   : $str - the string to pad.
6056 #############################################################################
6058 sub MakeReturnField {
6059     my ($str) = @_;
6061     return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6064 #############################################################################
6065 # Function    : GetSymbolSourceFile
6066 # Description : Get the filename where the symbol docs where taken from.
6067 # Arguments   : $symbol - the symbol name
6068 #############################################################################
6070 sub GetSymbolSourceFile {
6071     my ($symbol) = @_;
6073     if (defined($SourceSymbolSourceFile{$symbol})) {
6074         return $SourceSymbolSourceFile{$symbol};
6075     } elsif (defined($SymbolSourceFile{$symbol})) {
6076         return $SymbolSourceFile{$symbol};
6077     } else {
6078         return "";
6079     }
6082 #############################################################################
6083 # Function    : GetSymbolSourceLine
6084 # Description : Get the file line where the symbol docs where taken from.
6085 # Arguments   : $symbol - the symbol name
6086 #############################################################################
6088 sub GetSymbolSourceLine {
6089     my ($symbol) = @_;
6091     if (defined($SourceSymbolSourceLine{$symbol})) {
6092         return $SourceSymbolSourceLine{$symbol};
6093     } elsif (defined($SymbolSourceLine{$symbol})) {
6094         return $SymbolSourceLine{$symbol};
6095     } else {
6096         return 0;
6097     }