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