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 #############################################################################
32 push @INC, '@PACKAGE_DATA_DIR@';
33 require "gtkdoc-common.pl";
37 # name of documentation module
42 my $SOURCE_SUFFIXES = "";
43 my $IGNORE_FILES = "";
47 my $EXPAND_CONTENT_FILES = "";
48 my $INLINE_MARKUP_MODE;
49 my $DEFAULT_STABILITY;
53 my $OUTPUT_ALL_SYMBOLS;
54 my $OUTPUT_SYMBOLS_WITHOUT_SINCE;
58 my $PREREQUISITES_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.
83 my %DeclarationConditional;
84 my %DeclarationOutput;
90 # These global hashes store the existing documentation.
96 my %SymbolAnnotations;
98 # These global hashes store documentation scanned from the source files.
100 my %SourceSymbolParams;
101 my %SourceSymbolSourceFile;
102 my %SourceSymbolSourceLine;
104 # all documentation goes in here, so we can do coverage analysis
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).
122 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
123 # section they are defined
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 = (
152 # remember used annotation (to write minimal glossary)
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 'optional' => "NULL may be passed instead of a pointer to a location.",
161 'array' => "Parameter points to an array of items.",
162 'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
163 'attributes' => "Free-form key-value pairs.",
164 'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
165 'constructor' => "This symbol is a constructor, not a static method.",
166 'destroy' => "This parameter is a 'destroy_data', for callbacks.",
167 'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
168 'element-type' => "Generics and defining elements of containers and arrays.",
169 'error-domains' => "Typed errors. Similar to throws in Java.",
170 'foreign' => "This is a foreign struct.",
171 'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
172 'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
173 'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
174 'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
175 'method' => "This is a method",
176 'not-error' => "A GError parameter is not to be handled like a normal GError.",
177 'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
178 'out caller-allocates' => "Out parameter, where caller must allocate storage.",
179 'out callee-allocates' => "Out parameter, where caller must allocate storage.",
180 'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
181 'rename-to' => "Rename the original symbol's name to SYMBOL.",
182 'scope call' => "The callback is valid only during the call to the method.",
183 'scope async' => "The callback is valid until first called.",
184 'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
185 'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
186 'skip' => "Exposed in C code, not necessarily available in other languages.",
187 'transfer container' => "Free data container after the code is done.",
188 'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
189 'transfer full' => "Free data after the code is done.",
190 'transfer none' => "Don't free data after the code is done.",
191 'type' => "Override the parsed C type with given type.",
192 'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
193 'virtual' => "This is the invoker for a virtual method.",
194 'value' => "The specified value overrides the evaluated value of the constant.",
195 # Stability Level definition
196 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
198 The intention of a Stable interface is to enable arbitrary third parties to
199 develop applications to these interfaces, release them, and have confidence that
200 they will run on all minor releases of the product (after the one in which the
201 interface was introduced, and within the same major release). Even at a major
202 release, incompatible changes are expected to be rare, and to have strong
206 Unstable interfaces are experimental or transitional. They are typically used to
207 give outside developers early access to new or rapidly changing technology, or
208 to provide an interim solution to a problem where a more general solution is
209 anticipated. No claims are made about either source or binary compatibility from
210 one minor release to the next.
212 The Unstable interface level is a warning that these interfaces are subject to
213 change without warning and should not be used in unbundled products.
215 Given such caveats, customer impact need not be a factor when considering
216 incompatible changes to an Unstable interface in a major or minor release.
217 Nonetheless, when such changes are introduced, the changes should still be
218 mentioned in the release notes for the affected release.
221 An interface that can be used within the GNOME stack itself, but that is not
222 documented for end-users. Such functions should only be used in specified and
227 # Elements to consider non-block items in MarkDown parsing
228 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
242 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
258 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
261 # Function and other declaration output settings.
262 my $RETURN_TYPE_FIELD_WIDTH = 20;
263 my $SYMBOL_FIELD_WIDTH = 36;
264 my $MAX_SYMBOL_FIELD_WIDTH = 40;
265 my $SIGNAL_FIELD_WIDTH = 16;
266 my $PARAM_FIELD_COUNT = 2;
268 # XML, SGML formatting helper
272 run() unless caller; # Run program unless loaded as a module
276 my %optctl = ('module' => \$MODULE,
277 'source-dir' => \@SOURCE_DIRS,
278 'source-suffixes' => \$SOURCE_SUFFIXES,
279 'ignore-files' => \$IGNORE_FILES,
280 'output-dir' => \$DB_OUTPUT_DIR,
281 'tmpl-dir' => \$TMPL_DIR,
282 'version' => \$PRINT_VERSION,
283 'help' => \$PRINT_HELP,
284 'main-sgml-file' => \$MAIN_SGML_FILE,
285 'expand-content-files' => \$EXPAND_CONTENT_FILES,
286 'sgml-mode' => \$INLINE_MARKUP_MODE,
287 'xml-mode' => \$INLINE_MARKUP_MODE,
288 'default-stability' => \$DEFAULT_STABILITY,
289 'default-includes' => \$DEFAULT_INCLUDES,
290 'output-format' => \$OUTPUT_FORMAT,
291 'name-space' => \$NAME_SPACE,
292 'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
293 'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
295 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
296 "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version",
297 "outputallsymbols", "outputsymbolswithoutsince",
298 "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
299 "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
300 "output-format:s", "name-space:s");
302 if ($PRINT_VERSION) {
311 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
312 && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
318 gtkdoc-mkdb version @VERSION@ - generate docbook files
320 --module=MODULE_NAME Name of the doc module being parsed
321 --source-dir=DIRNAME Directories which contain inline reference material
322 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
323 --ignore-files=FILES A space-separated list of header files/dirs not to
325 --output-dir=DIRNAME Directory to put the generated DocBook files in
326 --tmpl-dir=DIRNAME Directory in which template files may be found
327 --main-sgml-file=FILE File containing the toplevel DocBook file.
328 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
329 --output-format=FORMAT Format to use for the generated docbook, XML or SGML.
330 --{xml,sgml}-mode Allow DocBook markup in inline documentation.
331 --default-stability=LEVEL Specify default stability Level. Valid values are
332 Stable, Unstable, or Private.
333 --default-includes=FILENAMES Specify default includes for section Synopsis
334 --name-space=NS Omit namespace in index.
335 --version Print the version of this program
336 --help Print this help
341 @TRACE@(" ignore files: [$IGNORE_FILES]\n");
343 # check output format
344 if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
345 $OUTPUT_FORMAT = "xml";
347 $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
349 if ($OUTPUT_FORMAT ne "xml") {
350 die "Invalid format '$OUTPUT_FORMAT' passed to --output.format"
353 if (!$MAIN_SGML_FILE) {
354 # backwards compatibility
355 if (-e "${MODULE}-docs.sgml") {
356 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
358 $MAIN_SGML_FILE = "${MODULE}-docs.xml";
362 # extract docbook header or define default
363 if (-e $MAIN_SGML_FILE) {
364 open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
365 $doctype_header = "";
367 if (/^\s*<(book|chapter|article)/) {
368 # check that the top-level tag or the doctype decl contain the xinclude namespace decl
369 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
370 $doctype_header = "";
374 $doctype_header .= $_;
377 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
378 # FIXME: not sure if we can do this now, as people already work-around the problem
379 # $doctype_header =~ s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#g;
381 $doctype_header = <<EOF;
382 <?xml version="1.0"?>
383 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
384 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
386 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
390 chomp($doctype_header);
392 # All the files are written in subdirectories beneath here.
393 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
395 # This is where we put all the DocBook output.
396 $DB_OUTPUT_DIR = $DB_OUTPUT_DIR ? $DB_OUTPUT_DIR : "$ROOT_DIR/xml";
398 # This file contains the object hierarchy.
399 $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
401 # This file contains the interfaces.
402 $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
404 # This file contains the prerequisites.
405 $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
407 # This file contains signal arguments and names.
408 $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
410 # The file containing Arg information.
411 $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
413 # Create the root DocBook output directory if it doens't exist.
414 if (! -e $DB_OUTPUT_DIR) {
415 mkdir ("$DB_OUTPUT_DIR", 0777)
416 || die "Can't create directory: $DB_OUTPUT_DIR";
419 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
420 &ReadSignalsFile ($SIGNALS_FILE);
421 &ReadArgsFile ($ARGS_FILE);
422 &ReadObjectHierarchy;
426 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
427 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
428 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
431 for my $dir (@SOURCE_DIRS) {
432 &ReadSourceDocumentation ($dir);
435 my $changed = &OutputDB ("$ROOT_DIR/$MODULE-sections.txt");
437 # If any of the DocBook files have changed, update the timestamp file (so
438 # it can be used for Makefile dependencies).
439 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
441 # try to detect the common prefix
442 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
443 if ($NAME_SPACE eq "") {
450 foreach my $symbol (keys(%IndexEntriesFull)) {
451 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
452 if (length($symbol)>$pos) {
453 $letter=substr($symbol,$pos,1);
454 # stop prefix scanning
455 if ($letter eq "_") {
459 # Should we also stop on a uppercase char, if last was lowercase
460 # GtkWidget, if we have the 'W' and had the 't' before
461 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
462 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
463 # need to recound each time as this is per symbol
464 $prefix{uc($letter)}++;
468 if ($letter ne "" && $letter ne "_") {
471 foreach $letter (keys(%prefix)) {
472 #print "$letter: $prefix{$letter}.\n";
473 if ($prefix{$letter}>$maxsymbols) {
475 $maxsymbols=$prefix{$letter};
478 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
479 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
482 $NAME_SPACE .= $maxletter;
489 } while ($ratio > 0.9);
490 #print "most symbols start with $NAME_SPACE\n";
494 &OutputDeprecatedIndex;
496 &OutputAnnotationGlossary;
498 open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
499 || die "Can't create $ROOT_DIR/sgml.stamp: $!";
500 print (TIMESTAMP "timestamp");
505 #############################################################################
506 # Function : OutputObjectList
507 # Description : This outputs the alphabetical list of objects, in a columned
509 # FIXME: Currently this also outputs ancestor objects
510 # which may not actually be in this module.
512 #############################################################################
514 sub OutputObjectList {
518 # my $old_object_index = "$DB_OUTPUT_DIR/object_index.xml";
519 my $old_object_index = "$DB_OUTPUT_DIR/object_index.sgml";
520 my $new_object_index = "$DB_OUTPUT_DIR/object_index.new";
522 open (OUTPUT, ">$new_object_index")
523 || die "Can't create $new_object_index: $!";
525 print (OUTPUT <<EOF);
526 ${\( MakeDocHeader ("informaltable") )}
527 <informaltable pgwide="1" frame="none">
528 <tgroup cols="$cols">
529 <colspec colwidth="1*"/>
530 <colspec colwidth="1*"/>
531 <colspec colwidth="1*"/>
537 foreach $object (sort (@Objects)) {
538 my $xref = &MakeXRef ($object);
539 if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
540 print (OUTPUT "<entry>$xref</entry>\n");
541 if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
545 # emit an empty row, since empty tables are invalid
546 print (OUTPUT "<row><entry> </entry></row>\n");
549 if ($count % $cols > 0) {
550 print (OUTPUT "</row>\n");
554 print (OUTPUT <<EOF);
555 </tbody></tgroup></informaltable>
559 &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
562 #############################################################################
563 # Function : TrimTextBlock
564 # Description : Trims extra whitespace. Empty lines inside a block are
566 # Arguments : $desc - the text block to trim. May contain newlines.
567 #############################################################################
572 # strip leading spaces on the block
574 # strip trailing spaces on every line
575 $desc =~ s/\s+$/\n/mg;
581 #############################################################################
582 # Function : OutputDB
583 # Description : This collects the output for each section of the docs, and
584 # outputs each file when the end of the section is found.
585 # Arguments : $file - the $MODULE-sections.txt file which contains all of
586 # the functions/macros/structs etc. being documented, organised
587 # into sections and subsections.
588 #############################################################################
593 @TRACE@("Reading: $file\n");
595 || die "Can't open $file: $!";
598 my $book_bottom = "";
599 my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
600 my $section_includes = "";
607 my $functions_synop = "";
608 my $other_synop = "";
609 my $functions_details = "";
610 my $other_details = "";
611 my $signals_synop = "";
612 my $signals_desc = "";
614 my $child_args_synop = "";
615 my $style_args_synop = "";
617 my $child_args_desc = "";
618 my $style_args_desc = "";
619 my $hierarchy_str = "";
622 my $implementations = "";
623 my $prerequisites = "";
625 my @file_objects = ();
627 my %symbol_def_line = ();
629 # merge the source docs, in case there are no templates
630 &MergeSourceDocumentation;
636 } elsif (m/^<SECTION>/) {
640 %symbol_def_line = ();
642 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
643 $other_synop .= "\n";
644 $functions_synop .= "\n";
647 } elsif (m/^<SUBSECTION>/) {
649 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
651 @TRACE@("Section: $title\n");
653 # We don't want warnings if object & class structs aren't used.
654 $DeclarationOutput{$title} = 1;
655 $DeclarationOutput{"${title}Class"} = 1;
656 $DeclarationOutput{"${title}Iface"} = 1;
657 $DeclarationOutput{"${title}Interface"} = 1;
659 } elsif (m/^<FILE>(.*)<\/FILE>/) {
661 if (! defined $templates{$filename}) {
662 if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
663 &MergeSourceDocumentation;
664 $templates{$filename}=$.;
667 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
668 "Previous occurrence on line ".$templates{$filename}.".");
670 if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
671 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
672 # Remove trailing blanks
676 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
678 $section_includes = $1;
680 if (defined $DEFAULT_INCLUDES) {
681 &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
688 } elsif (m/^<\/SECTION>/) {
689 @TRACE@("End of section: $title\n");
690 if ($num_symbols > 0) {
692 $book_bottom .= " <xi:include href=\"xml/$filename.xml\"/>\n";
694 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
695 if ($section_includes) {
696 &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
698 $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
700 if ($section_includes eq "") {
701 $section_includes = $includes;
704 $signals_synop =~ s/^\n*//g;
705 $signals_synop =~ s/\n+$/\n/g;
706 if ($signals_synop ne '') {
707 $signals_synop = <<EOF;
708 <refsect1 id="$section_id.signals" role="signal_proto">
709 <title role="signal_proto.title">Signals</title>
710 <informaltable frame="none">
712 <colspec colname="signals_return" colwidth="150px"/>
713 <colspec colname="signals_name" colwidth="300px"/>
714 <colspec colname="signals_flags" colwidth="200px"/>
722 $signals_desc = TrimTextBlock($signals_desc);
723 $signals_desc = <<EOF;
724 <refsect1 id="$section_id.signal-details" role="signals">
725 <title role="signals.title">Signal Details</title>
731 $args_synop =~ s/^\n*//g;
732 $args_synop =~ s/\n+$/\n/g;
733 if ($args_synop ne '') {
735 <refsect1 id="$section_id.properties" role="properties">
736 <title role="properties.title">Properties</title>
737 <informaltable frame="none">
739 <colspec colname="properties_type" colwidth="150px"/>
740 <colspec colname="properties_name" colwidth="300px"/>
741 <colspec colname="properties_flags" colwidth="200px"/>
749 $args_desc = TrimTextBlock($args_desc);
751 <refsect1 id="$section_id.property-details" role="property_details">
752 <title role="property_details.title">Property Details</title>
758 $child_args_synop =~ s/^\n*//g;
759 $child_args_synop =~ s/\n+$/\n/g;
760 if ($child_args_synop ne '') {
761 $args_synop .= <<EOF;
762 <refsect1 id="$section_id.child-properties" role="child_properties">
763 <title role="child_properties.title">Child Properties</title>
764 <informaltable frame="none">
766 <colspec colname="child_properties_type" colwidth="150px"/>
767 <colspec colname="child_properties_name" colwidth="300px"/>
768 <colspec colname="child_properties_flags" colwidth="200px"/>
776 $child_args_desc = TrimTextBlock($child_args_desc);
778 <refsect1 id="$section_id.child-property-details" role="child_property_details">
779 <title role="child_property_details.title">Child Property Details</title>
785 $style_args_synop =~ s/^\n*//g;
786 $style_args_synop =~ s/\n+$/\n/g;
787 if ($style_args_synop ne '') {
788 $args_synop .= <<EOF;
789 <refsect1 id="$section_id.style-properties" role="style_properties">
790 <title role="style_properties.title">Style Properties</title>
791 <informaltable frame="none">
793 <colspec colname="style_properties_type" colwidth="150px"/>
794 <colspec colname="style_properties_name" colwidth="300px"/>
795 <colspec colname="style_properties_flags" colwidth="200px"/>
803 $style_args_desc = TrimTextBlock($style_args_desc);
805 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
806 <title role="style_properties_details.title">Style Property Details</title>
812 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
813 if ($hierarchy_str ne "") {
814 $hierarchy_str = <<EOF;
815 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
816 <title role="object_hierarchy.title">Object Hierarchy</title>
817 <screen>$hierarchy_str
823 $interfaces =~ TrimTextBlock($interfaces);
824 if ($interfaces ne "") {
826 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
827 <title role="impl_interfaces.title">Implemented Interfaces</title>
833 $implementations = TrimTextBlock($implementations);
834 if ($implementations ne "") {
835 $implementations = <<EOF;
836 <refsect1 id="$section_id.implementations" role="implementations">
837 <title role="implementations.title">Known Implementations</title>
843 $prerequisites = TrimTextBlock($prerequisites);
844 if ($prerequisites ne "") {
845 $prerequisites = <<EOF;
846 <refsect1 id="$section_id.prerequisites" role="prerequisites">
847 <title role="prerequisites.title">Prerequisites</title>
853 $derived = TrimTextBlock($derived);
854 if ($derived ne "") {
856 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
857 <title role="derived_interfaces.title">Known Derived Interfaces</title>
863 $functions_synop =~ s/^\n*//g;
864 $functions_synop =~ s/\n+$/\n/g;
865 if ($functions_synop ne '') {
866 $functions_synop = <<EOF;
867 <refsect1 id="$section_id.functions" role="functions_proto">
868 <title role="functions_proto.title">Functions</title>
869 <informaltable pgwide="1" frame="none">
871 <colspec colname="functions_return" colwidth="150px"/>
872 <colspec colname="functions_name"/>
882 $other_synop =~ s/^\n*//g;
883 $other_synop =~ s/\n+$/\n/g;
884 if ($other_synop ne '') {
885 $other_synop = <<EOF;
886 <refsect1 id="$section_id.other" role="other_proto">
887 <title role="other_proto.title">Types and Values</title>
888 <informaltable role="enum_members_table" pgwide="1" frame="none">
890 <colspec colname="name" colwidth="150px"/>
891 <colspec colname="description"/>
901 my $file_changed = &OutputDBFile ($filename, $title, $section_id,
903 \$functions_synop, \$other_synop,
904 \$functions_details, \$other_details,
905 \$signals_synop, \$signals_desc,
906 \$args_synop, \$args_desc,
907 \$hierarchy_str, \$interfaces,
909 \$prerequisites, \$derived,
919 $section_includes = "";
920 $functions_synop = "";
922 $functions_details = "";
927 $child_args_synop = "";
928 $style_args_synop = "";
930 $child_args_desc = "";
931 $style_args_desc = "";
935 $implementations = "";
939 } elsif (m/^(\S+)/) {
941 @TRACE@(" Symbol: $symbol in subsection: $subsection\n");
943 # check for duplicate entries
944 if (! defined $symbol_def_line{$symbol}) {
945 my $declaration = $Declarations{$symbol};
946 if (defined ($declaration)) {
947 if (&CheckIsObject ($symbol)) {
948 push @file_objects, $symbol;
950 # We don't want standard macros/functions of GObjects,
951 # or private declarations.
952 if ($subsection ne "Standard" && $subsection ne "Private") {
953 my ($synop, $desc) = &OutputDeclaration ($symbol,
955 my $type = $DeclarationTypes {$symbol};
957 if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
958 $functions_synop .= $synop;
959 $functions_details .= $desc;
960 } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
961 $functions_synop .= $synop;
962 $functions_details .= $desc;
964 $other_synop .= $synop;
965 $other_details .= $desc;
968 my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
969 my ($arg_synop, $child_arg_synop, $style_arg_synop,
970 $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
971 my $ifaces = &GetInterfaces ($symbol);
972 my $impls = &GetImplementations ($symbol);
973 my $prereqs = &GetPrerequisites ($symbol);
974 my $der = &GetDerived ($symbol);
975 @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
977 $signals_synop .= $sig_synop;
978 $signals_desc .= $sig_desc;
979 $args_synop .= $arg_synop;
980 $child_args_synop .= $child_arg_synop;
981 $style_args_synop .= $style_arg_synop;
982 $args_desc .= $arg_desc;
983 $child_args_desc .= $child_arg_desc;
984 $style_args_desc .= $style_arg_desc;
985 $interfaces .= $ifaces;
986 $implementations .= $impls;
987 $prerequisites .= $prereqs;
990 # Note that the declaration has been output.
991 $DeclarationOutput{$symbol} = 1;
992 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
993 $UndeclaredSymbols{$symbol} = 1;
994 &LogWarning ($file, $., "No declaration found for $symbol.");
997 $symbol_def_line{$symbol}=$.;
999 if ($section_id eq "") {
1000 if($title eq "" && $filename eq "") {
1001 &LogWarning ($file, $., "Section has no title and no file.");
1003 # FIXME: one of those would be enough
1004 # filename should be an internal detail for gtk-doc
1007 } elsif ($filename eq "") {
1010 $filename =~ s/\s/_/g;
1012 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1013 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1014 # Remove trailing blanks and use as is
1015 $section_id =~ s/\s+$//;
1016 } elsif (&CheckIsObject ($title)) {
1017 # GObjects use their class name as the ID.
1018 $section_id = &CreateValidSGMLID ($title);
1020 $section_id = &CreateValidSGMLID ("$MODULE-$title");
1023 $SymbolSection{$symbol}=$title;
1024 $SymbolSectionId{$symbol}=$section_id;
1027 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1028 "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1034 &OutputMissingDocumentation;
1035 &OutputUndeclaredSymbols;
1036 &OutputUnusedSymbols;
1038 if ($OUTPUT_ALL_SYMBOLS) {
1041 if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1042 &OutputSymbolsWithoutSince;
1045 for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1046 my $file_changed = &OutputExtraFile ($filename);
1047 if ($file_changed) {
1052 &OutputBook ($book_top, $book_bottom);
1057 #############################################################################
1058 # Function : OutputIndex
1059 # Description : This writes an indexlist that can be included into the main-
1060 # document into an <index> tag.
1061 #############################################################################
1064 my ($basename, $apiindexref ) = @_;
1065 my %apiindex = %{$apiindexref};
1066 my $old_index = "$DB_OUTPUT_DIR/$basename.xml";
1067 my $new_index = "$DB_OUTPUT_DIR/$basename.new";
1068 my $lastletter = " ";
1073 open (OUTPUT, ">$new_index")
1074 || die "Can't create $new_index";
1076 print (OUTPUT &MakeDocHeader ("indexdiv")."\n<indexdiv id=\"$basename\">\n");
1078 @TRACE@("generate $basename index (".%apiindex." entries)\n");
1080 # do a case insensitive sort while chopping off the prefix
1082 sort { $$a{criteria} cmp $$b{criteria} or $$a{original} cmp $$b{original} }
1083 map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1086 $symbol = $$hash{original};
1087 if (defined($$hash{short})) {
1088 $short_symbol = $$hash{short};
1090 $short_symbol = $symbol;
1093 # generate a short symbol description
1094 my $symbol_desc = "";
1095 my $symbol_section = "";
1096 my $symbol_section_id = "";
1097 my $symbol_type = "";
1098 if (defined($DeclarationTypes{$symbol})) {
1099 $symbol_type = lc($DeclarationTypes{$symbol});
1101 if ($symbol_type eq "") {
1102 @TRACE@("trying symbol $symbol\n");
1103 if ($symbol =~ m/(.*)::(.*)/) {
1107 @TRACE@(" trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1108 for ($i = 0; $i <= $#SignalNames; $i++) {
1109 if ($SignalNames[$i] eq $osym) {
1110 $symbol_type = "object signal";
1111 if (defined($SymbolSection{$oname})) {
1112 $symbol_section = $SymbolSection{$oname};
1113 $symbol_section_id = $SymbolSectionId{$oname};
1118 } elsif ($symbol =~ m/(.*):(.*)/) {
1122 @TRACE@(" trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1123 for ($i = 0; $i <= $#ArgNames; $i++) {
1124 @TRACE@(" ".$ArgNames[$i]."\n");
1125 if ($ArgNames[$i] eq $osym) {
1126 $symbol_type = "object property";
1127 if (defined($SymbolSection{$oname})) {
1128 $symbol_section = $SymbolSection{$oname};
1129 $symbol_section_id = $SymbolSectionId{$oname};
1136 if (defined($SymbolSection{$symbol})) {
1137 $symbol_section = $SymbolSection{$symbol};
1138 $symbol_section_id = $SymbolSectionId{$symbol};
1141 if ($symbol_type ne "") {
1142 $symbol_desc=", $symbol_type";
1143 if ($symbol_section ne "") {
1144 $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1145 #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1149 my $curletter = uc(substr($short_symbol,0,1));
1150 my $id = $apiindex{$symbol};
1152 @TRACE@(" add symbol $symbol with $id to index in section $curletter\n");
1154 if ($curletter ne $lastletter) {
1155 $lastletter = $curletter;
1157 if ($divopen == 1) {
1158 print (OUTPUT "</indexdiv>\n");
1160 print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1164 print (OUTPUT <<EOF);
1165 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1169 if ($divopen == 1) {
1170 print (OUTPUT "</indexdiv>\n");
1172 print (OUTPUT "</indexdiv>\n");
1175 &UpdateFileIfChanged ($old_index, $new_index, 0);
1179 #############################################################################
1180 # Function : OutputIndexFull
1181 # Description : This writes the full api indexlist that can be included into the
1182 # main document into an <index> tag.
1183 #############################################################################
1185 sub OutputIndexFull {
1186 &OutputIndex ("api-index-full", \%IndexEntriesFull);
1190 #############################################################################
1191 # Function : OutputDeprecatedIndex
1192 # Description : This writes the deprecated api indexlist that can be included
1193 # into the main document into an <index> tag.
1194 #############################################################################
1196 sub OutputDeprecatedIndex {
1197 &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1201 #############################################################################
1202 # Function : OutputSinceIndexes
1203 # Description : This writes the 'since' api indexlists that can be included into
1204 # the main document into an <index> tag.
1205 #############################################################################
1207 sub OutputSinceIndexes {
1208 my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1210 foreach my $version (@sinces) {
1211 @TRACE@("Since : [$version]\n");
1212 # TODO make filtered hash
1213 #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1214 my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1216 &OutputIndex ("api-index-$version", \%index);
1220 #############################################################################
1221 # Function : OutputAnnotationGlossary
1222 # Description : This writes a glossary of the used annotation terms into a
1223 # separate glossary file that can be included into the main
1225 #############################################################################
1227 sub OutputAnnotationGlossary {
1228 my $old_glossary = "$DB_OUTPUT_DIR/annotation-glossary.xml";
1229 my $new_glossary = "$DB_OUTPUT_DIR/annotation-glossary.new";
1230 my $lastletter = " ";
1233 # if there are no annotations used return
1234 return if (! keys(%AnnotationsUsed));
1236 # add acronyms that are referenced from acronym text
1238 foreach my $annotation (keys(%AnnotationsUsed)) {
1239 if(defined($AnnotationDefinition{$annotation})) {
1240 if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1241 if (!exists($AnnotationsUsed{$1})) {
1242 $AnnotationsUsed{$1} = 1;
1249 open (OUTPUT, ">$new_glossary")
1250 || die "Can't create $new_glossary";
1252 print (OUTPUT <<EOF);
1253 ${\( MakeDocHeader ("glossary") )}
1254 <glossary id="annotation-glossary">
1255 <title>Annotation Glossary</title>
1258 foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1259 if(defined($AnnotationDefinition{$annotation})) {
1260 my $def = $AnnotationDefinition{$annotation};
1261 my $curletter = uc(substr($annotation,0,1));
1263 if ($curletter ne $lastletter) {
1264 $lastletter = $curletter;
1266 if ($divopen == 1) {
1267 print (OUTPUT "</glossdiv>\n");
1269 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1272 print (OUTPUT <<EOF);
1274 <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1283 if ($divopen == 1) {
1284 print (OUTPUT "</glossdiv>\n");
1286 print (OUTPUT "</glossary>\n");
1289 &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1292 #############################################################################
1293 # Function : ReadKnownSymbols
1294 # Description : This collects the names of non-private symbols from the
1295 # $MODULE-sections.txt file.
1296 # Arguments : $file - the $MODULE-sections.txt file which contains all of
1297 # the functions/macros/structs etc. being documented, organised
1298 # into sections and subsections.
1299 #############################################################################
1301 sub ReadKnownSymbols {
1304 my $subsection = "";
1306 @TRACE@("Reading: $file\n");
1308 || die "Can't open $file: $!";
1314 } elsif (m/^<SECTION>/) {
1317 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1320 } elsif (m/^<SUBSECTION>/) {
1323 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1326 } elsif (m/^<FILE>(.*)<\/FILE>/) {
1327 $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1328 $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1331 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1334 } elsif (m/^<\/SECTION>/) {
1337 } elsif (m/^(\S+)/) {
1340 if ($subsection ne "Standard" && $subsection ne "Private") {
1341 $KnownSymbols{$symbol} = 1;
1344 $KnownSymbols{$symbol} = 0;
1352 #############################################################################
1353 # Function : OutputDeclaration
1354 # Description : Returns the synopsis and detailed description DocBook
1355 # describing one function/macro etc.
1356 # Arguments : $symbol - the name of the function/macro begin described.
1357 # $declaration - the declaration of the function/macro.
1358 #############################################################################
1360 sub OutputDeclaration {
1361 my ($symbol, $declaration) = @_;
1363 my $type = $DeclarationTypes {$symbol};
1364 if ($type eq 'MACRO') {
1365 return &OutputMacro ($symbol, $declaration);
1366 } elsif ($type eq 'TYPEDEF') {
1367 return &OutputTypedef ($symbol, $declaration);
1368 } elsif ($type eq 'STRUCT') {
1369 return &OutputStruct ($symbol, $declaration);
1370 } elsif ($type eq 'ENUM') {
1371 return &OutputEnum ($symbol, $declaration);
1372 } elsif ($type eq 'UNION') {
1373 return &OutputUnion ($symbol, $declaration);
1374 } elsif ($type eq 'VARIABLE') {
1375 return &OutputVariable ($symbol, $declaration);
1376 } elsif ($type eq 'FUNCTION') {
1377 return &OutputFunction ($symbol, $declaration, $type);
1378 } elsif ($type eq 'USER_FUNCTION') {
1379 return &OutputFunction ($symbol, $declaration, $type);
1381 die "Unknown symbol type";
1386 #############################################################################
1387 # Function : OutputSymbolTraits
1388 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1389 # Arguments : $symbol - the name of the function/macro begin described.
1390 #############################################################################
1392 sub OutputSymbolTraits {
1396 if (exists $Since{$symbol}) {
1397 my $link_id = "api-index-".$Since{$symbol};
1398 $desc .= "<para role=\"since\">Since: <link linkend=\"$link_id\">$Since{$symbol}</link></para>";
1400 if (exists $StabilityLevel{$symbol}) {
1401 my $stability = $StabilityLevel{$symbol};
1402 $AnnotationsUsed{$stability} = 1;
1403 $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1408 #############################################################################
1409 # Function : Output{Symbol,Section}ExtraLinks
1410 # Description : Returns extralinks for the symbol (if enabled).
1411 # Arguments : $symbol - the name of the function/macro begin described.
1412 #############################################################################
1416 return undef unless defined $text;
1418 # Build a char to hex map
1421 $escapes{chr($_)} = sprintf("%%%02X", $_);
1424 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1425 $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1430 sub OutputSymbolExtraLinks {
1434 if (0) { # NEW FEATURE: needs configurability
1435 my $sstr = &uri_escape($symbol);
1436 my $mstr = &uri_escape($MODULE);
1438 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1439 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$sstr">edit documentation</ulink>
1445 sub OutputSectionExtraLinks {
1446 my ($symbol,$docsymbol) = @_;
1449 if (0) { # NEW FEATURE: needs configurability
1450 my $sstr = &uri_escape($symbol);
1451 my $mstr = &uri_escape($MODULE);
1452 my $dsstr = &uri_escape($docsymbol);
1454 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1455 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$dsstr">edit documentation</ulink>
1462 #############################################################################
1463 # Function : OutputMacro
1464 # Description : Returns the synopsis and detailed description of a macro.
1465 # Arguments : $symbol - the macro.
1466 # $declaration - the declaration of the macro.
1467 #############################################################################
1470 my ($symbol, $declaration) = @_;
1471 my $id = &CreateValidSGMLID ($symbol);
1472 my $condition = &MakeConditionDescription ($symbol);
1473 my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1476 my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1477 my $title = $symbol . (@fields ? "()" : "");
1479 $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1480 $desc .= MakeIndexterms($symbol, $id);
1482 $desc .= OutputSymbolExtraLinks($symbol);
1485 $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1487 $synop .= "</entry></row>\n";
1489 # Don't output the macro definition if is is a conditional macro or it
1490 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1491 # longer than 2 lines, otherwise we get lots of complicated macros like
1493 if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1494 && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1495 my $decl_out = &CreateValidSGML ($declaration);
1496 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1498 $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1499 if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1501 my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1502 # Align each line so that if should all line up OK.
1503 $args =~ s/\n/\n$pad/gm;
1504 $desc .= &CreateValidSGML ($args);
1506 $desc .= "</programlisting>\n";
1509 $desc .= &MakeDeprecationNote($symbol);
1511 my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1513 if (defined ($SymbolDocs{$symbol})) {
1514 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1515 $desc .= $symbol_docs;
1518 $desc .= $parameters;
1519 $desc .= OutputSymbolTraits ($symbol);
1520 $desc .= "</refsect2>\n";
1521 return ($synop, $desc);
1525 #############################################################################
1526 # Function : OutputTypedef
1527 # Description : Returns the synopsis and detailed description of a typedef.
1528 # Arguments : $symbol - the typedef.
1529 # $declaration - the declaration of the typedef,
1530 # e.g. 'typedef unsigned int guint;'
1531 #############################################################################
1534 my ($symbol, $declaration) = @_;
1535 my $id = &CreateValidSGMLID ($symbol);
1536 my $condition = &MakeConditionDescription ($symbol);
1537 my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1538 my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1540 $desc .= MakeIndexterms($symbol, $id);
1542 $desc .= OutputSymbolExtraLinks($symbol);
1544 if (!defined ($DeclarationConditional{$symbol})) {
1545 my $decl_out = &CreateValidSGML ($declaration);
1546 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1549 $desc .= &MakeDeprecationNote($symbol);
1551 if (defined ($SymbolDocs{$symbol})) {
1552 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1554 $desc .= OutputSymbolTraits ($symbol);
1555 $desc .= "</refsect2>\n";
1556 return ($synop, $desc);
1560 #############################################################################
1561 # Function : OutputStruct
1562 # Description : Returns the synopsis and detailed description of a struct.
1563 # We check if it is a object struct, and if so we only output
1564 # parts of it that are noted as public fields.
1565 # We also use a different IDs for object structs, since the
1566 # original ID is used for the entire RefEntry.
1567 # Arguments : $symbol - the struct.
1568 # $declaration - the declaration of the struct.
1569 #############################################################################
1572 my ($symbol, $declaration) = @_;
1575 my $default_to_public = 1;
1576 if (&CheckIsObject ($symbol)) {
1577 @TRACE@("Found struct gtype: $symbol\n");
1579 $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1585 $id = &CreateValidSGMLID ($symbol . "_struct");
1586 $condition = &MakeConditionDescription ($symbol . "_struct");
1588 $id = &CreateValidSGMLID ($symbol);
1589 $condition = &MakeConditionDescription ($symbol);
1592 # Determine if it is a simple struct or it also has a typedef.
1593 my $has_typedef = 0;
1594 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1601 # For structs with typedefs we just output the struct name.
1603 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1605 $type_output = "struct";
1606 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1608 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1610 $desc .= MakeIndexterms($symbol, $id);
1612 $desc .= OutputSymbolExtraLinks($symbol);
1614 # Form a pretty-printed, private-data-removed form of the declaration
1617 if ($declaration =~ m/^\s*$/) {
1618 @TRACE@("Found opaque struct: $symbol\n");
1619 $decl_out = "typedef struct _$symbol $symbol;";
1620 } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1621 @TRACE@("Found opaque struct: $symbol\n");
1622 $decl_out = "struct $symbol;";
1624 my $public = $default_to_public;
1625 my $new_declaration = "";
1627 my $decl = $declaration;
1629 if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1630 my $struct_contents = $2;
1632 foreach $decl_line (split (/\n/, $struct_contents)) {
1633 @TRACE@("Struct line: $decl_line\n");
1634 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1636 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1639 $new_declaration .= $decl_line . "\n";
1643 if ($new_declaration) {
1644 # Strip any blank lines off the ends.
1645 $new_declaration =~ s/^\s*\n//;
1646 $new_declaration =~ s/\n\s*$/\n/;
1649 $decl_out = "typedef struct {\n" . $new_declaration
1652 $decl_out = "struct $symbol {\n" . $new_declaration
1657 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1658 "Couldn't parse struct:\n$declaration");
1661 # If we couldn't parse the struct or it was all private, output an
1662 # empty struct declaration.
1663 if ($decl_out eq "") {
1665 $decl_out = "typedef struct _$symbol $symbol;";
1667 $decl_out = "struct $symbol;";
1672 $decl_out = &CreateValidSGML ($decl_out);
1673 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1675 $desc .= &MakeDeprecationNote($symbol);
1677 if (defined ($SymbolDocs{$symbol})) {
1678 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1681 # Create a table of fields and descriptions
1683 # FIXME: Inserting  's into the produced type declarations here would
1684 # improve the output in most situations ... except for function
1685 # members of structs!
1686 my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1689 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1691 my $params = $SymbolParams{$symbol};
1693 # If no parameters are filled in, we don't generate the description
1694 # table, for backwards compatibility.
1697 if (defined $params) {
1698 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1699 if ($params->[$i] =~ /\S/) {
1707 my %field_descrs = @$params;
1708 my $missing_parameters = "";
1709 my $unused_parameters = "";
1712 <refsect3 role="struct_members">\n<title>Members</title>
1713 <informaltable role="struct_members_table" pgwide="1" frame="none">
1715 <colspec colname="struct_members_name" colwidth="300px"/>
1716 <colspec colname="struct_members_description"/>
1717 <colspec colname="struct_members_annotations" colwidth="200px"/>
1722 my $field_name = shift @fields;
1723 my $text = shift @fields;
1724 my $field_descr = $field_descrs{$field_name};
1725 my $param_annotations = "";
1727 $desc .= "<row><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1728 if (defined $field_descr) {
1729 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1730 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1732 $field_descr =~ s/^(\s|\n)+//msg;
1733 $field_descr =~ s/(\s|\n)+$//msg;
1734 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1735 delete $field_descrs{$field_name};
1737 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1738 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1739 if ($missing_parameters ne "") {
1740 $missing_parameters .= ", ".$field_name;
1742 $missing_parameters = $field_name;
1744 $desc .= "<entry /><entry />\n";
1746 $desc .= "</row>\n";
1748 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1749 foreach my $field_name (keys %field_descrs) {
1750 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1751 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1752 if ($unused_parameters ne "") {
1753 $unused_parameters .= ", ".$field_name;
1755 $unused_parameters = $field_name;
1759 # remember missing/unused parameters (needed in tmpl-free build)
1760 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1761 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1763 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1764 $AllUnusedSymbols{$symbol}=$unused_parameters;
1768 if (scalar(@fields) > 0) {
1769 if (! exists ($AllIncompleteSymbols{$symbol})) {
1770 $AllIncompleteSymbols{$symbol}="<items>";
1771 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1772 "Field descriptions for struct $symbol are missing in source code comment block.");
1773 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1778 $desc .= OutputSymbolTraits ($symbol);
1779 $desc .= "</refsect2>\n";
1780 return ($synop, $desc);
1784 #############################################################################
1785 # Function : OutputUnion
1786 # Description : Returns the synopsis and detailed description of a union.
1787 # Arguments : $symbol - the union.
1788 # $declaration - the declaration of the union.
1789 #############################################################################
1792 my ($symbol, $declaration) = @_;
1795 if (&CheckIsObject ($symbol)) {
1796 @TRACE@("Found union gtype: $symbol\n");
1803 $id = &CreateValidSGMLID ($symbol . "_union");
1804 $condition = &MakeConditionDescription ($symbol . "_union");
1806 $id = &CreateValidSGMLID ($symbol);
1807 $condition = &MakeConditionDescription ($symbol);
1810 # Determine if it is a simple struct or it also has a typedef.
1811 my $has_typedef = 0;
1812 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1819 # For unions with typedefs we just output the union name.
1821 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1823 $type_output = "union";
1824 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1826 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1828 $desc .= MakeIndexterms($symbol, $id);
1830 $desc .= OutputSymbolExtraLinks($symbol);
1831 $desc .= &MakeDeprecationNote($symbol);
1833 if (defined ($SymbolDocs{$symbol})) {
1834 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1837 # Create a table of fields and descriptions
1839 # FIXME: Inserting  's into the produced type declarations here would
1840 # improve the output in most situations ... except for function
1841 # members of structs!
1842 my @fields = ParseStructDeclaration($declaration, 0,
1845 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1847 my $params = $SymbolParams{$symbol};
1849 # If no parameters are filled in, we don't generate the description
1850 # table, for backwards compatibility
1853 if (defined $params) {
1854 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1855 if ($params->[$i] =~ /\S/) {
1863 my %field_descrs = @$params;
1864 my $missing_parameters = "";
1865 my $unused_parameters = "";
1868 <refsect3 role="union_members">\n<title>Members</title>
1869 <informaltable role="union_members_table" pgwide="1" frame="none">
1871 <colspec colname="union_members_name" colwidth="300px"/>
1872 <colspec colname="union_members_description"/>
1873 <colspec colname="union_members_annotations" colwidth="200px"/>
1878 my $field_name = shift @fields;
1879 my $text = shift @fields;
1880 my $field_descr = $field_descrs{$field_name};
1881 my $param_annotations = "";
1883 $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1884 if (defined $field_descr) {
1885 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1886 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1889 $field_descr =~ s/^(\s|\n)+//msg;
1890 $field_descr =~ s/(\s|\n)+$//msg;
1891 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1892 delete $field_descrs{$field_name};
1894 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1895 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1896 if ($missing_parameters ne "") {
1897 $missing_parameters .= ", ".$field_name;
1899 $missing_parameters = $field_name;
1901 $desc .= "<entry /><entry />\n";
1903 $desc .= "</row>\n";
1905 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1906 foreach my $field_name (keys %field_descrs) {
1907 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1908 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1909 if ($unused_parameters ne "") {
1910 $unused_parameters .= ", ".$field_name;
1912 $unused_parameters = $field_name;
1916 # remember missing/unused parameters (needed in tmpl-free build)
1917 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1918 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1920 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1921 $AllUnusedSymbols{$symbol}=$unused_parameters;
1925 if (scalar(@fields) > 0) {
1926 if (! exists ($AllIncompleteSymbols{$symbol})) {
1927 $AllIncompleteSymbols{$symbol}="<items>";
1928 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1929 "Field descriptions for union $symbol are missing in source code comment block.");
1930 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1935 $desc .= OutputSymbolTraits ($symbol);
1936 $desc .= "</refsect2>\n";
1937 return ($synop, $desc);
1941 #############################################################################
1942 # Function : OutputEnum
1943 # Description : Returns the synopsis and detailed description of a enum.
1944 # Arguments : $symbol - the enum.
1945 # $declaration - the declaration of the enum.
1946 #############################################################################
1949 my ($symbol, $declaration) = @_;
1952 if (&CheckIsObject ($symbol)) {
1953 @TRACE@("Found enum gtype: $symbol\n");
1960 $id = &CreateValidSGMLID ($symbol . "_enum");
1961 $condition = &MakeConditionDescription ($symbol . "_enum");
1963 $id = &CreateValidSGMLID ($symbol);
1964 $condition = &MakeConditionDescription ($symbol);
1967 my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1968 my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1970 $desc .= MakeIndexterms($symbol, $id);
1972 $desc .= OutputSymbolExtraLinks($symbol);
1973 $desc .= &MakeDeprecationNote($symbol);
1975 if (defined ($SymbolDocs{$symbol})) {
1976 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1979 # Create a table of fields and descriptions
1981 my @fields = ParseEnumDeclaration($declaration);
1982 my $params = $SymbolParams{$symbol};
1984 # If nothing at all is documented log a single summary warning at the end.
1985 # Otherwise, warn about each undocumented item.
1988 if (defined $params) {
1989 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1990 if ($params->[$i] =~ /\S/) {
1997 my %field_descrs = (defined $params ? @$params : ());
1998 my $missing_parameters = "";
1999 my $unused_parameters = "";
2002 <refsect3 role="enum_members">\n<title>Members</title>
2003 <informaltable role="enum_members_table" pgwide="1" frame="none">
2005 <colspec colname="enum_members_name" colwidth="300px"/>
2006 <colspec colname="enum_members_description"/>
2007 <colspec colname="enum_members_annotations" colwidth="200px"/>
2011 for my $field_name (@fields) {
2012 my $field_descr = $field_descrs{$field_name};
2013 my $param_annotations = "";
2015 $id = &CreateValidSGMLID ($field_name);
2016 $condition = &MakeConditionDescription ($field_name);
2017 $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2018 if (defined $field_descr) {
2019 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2020 $field_descr = &ConvertMarkDown($symbol, $field_descr);
2021 $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2022 delete $field_descrs{$field_name};
2025 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2026 "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2027 if ($missing_parameters ne "") {
2028 $missing_parameters .= ", ".$field_name;
2030 $missing_parameters = $field_name;
2033 $desc .= "<entry /><entry />\n";
2035 $desc .= "</row>\n";
2037 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2038 foreach my $field_name (keys %field_descrs) {
2039 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2040 "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2041 if ($unused_parameters ne "") {
2042 $unused_parameters .= ", ".$field_name;
2044 $unused_parameters = $field_name;
2048 # remember missing/unused parameters (needed in tmpl-free build)
2049 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2050 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2052 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2053 $AllUnusedSymbols{$symbol}=$unused_parameters;
2057 if (scalar(@fields) > 0) {
2058 if (! exists ($AllIncompleteSymbols{$symbol})) {
2059 $AllIncompleteSymbols{$symbol}="<items>";
2060 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2061 "Value descriptions for $symbol are missing in source code comment block.");
2066 $desc .= OutputSymbolTraits ($symbol);
2067 $desc .= "</refsect2>\n";
2068 return ($synop, $desc);
2072 #############################################################################
2073 # Function : OutputVariable
2074 # Description : Returns the synopsis and detailed description of a variable.
2075 # Arguments : $symbol - the extern'ed variable.
2076 # $declaration - the declaration of the variable.
2077 #############################################################################
2079 sub OutputVariable {
2080 my ($symbol, $declaration) = @_;
2081 my $id = &CreateValidSGMLID ($symbol);
2082 my $condition = &MakeConditionDescription ($symbol);
2084 @TRACE@("ouputing variable: '$symbol' '$declaration'");
2087 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*;/) {
2088 my $mod1 = defined ($1) ? $1 : "";
2089 my $ptr = defined ($3) ? $3 : "";
2090 my $space = defined ($4) ? $4 : "";
2091 my $mod2 = defined ($5) ? $5 : "";
2092 $type_output = "extern $mod1$ptr$space$mod2";
2093 } 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*=/) {
2094 my $mod1 = defined ($1) ? $1 : "";
2095 my $ptr = defined ($3) ? $3 : "";
2096 my $space = defined ($4) ? $4 : "";
2097 my $mod2 = defined ($5) ? $5 : "";
2098 $type_output = "$mod1$ptr$space$mod2";
2100 $type_output = "extern";
2102 my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2104 my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2106 $desc .= MakeIndexterms($symbol, $id);
2108 $desc .= OutputSymbolExtraLinks($symbol);
2110 my $decl_out = &CreateValidSGML ($declaration);
2111 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2113 $desc .= &MakeDeprecationNote($symbol);
2115 if (defined ($SymbolDocs{$symbol})) {
2116 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2118 if (defined ($SymbolAnnotations{$symbol})) {
2119 my $param_desc = $SymbolAnnotations{$symbol};
2120 my $param_annotations = "";
2121 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2122 if ($param_annotations ne "") {
2123 $desc .= "\n<para>$param_annotations</para>";
2127 $desc .= OutputSymbolTraits ($symbol);
2128 $desc .= "</refsect2>\n";
2129 return ($synop, $desc);
2133 #############################################################################
2134 # Function : OutputFunction
2135 # Description : Returns the synopsis and detailed description of a function.
2136 # Arguments : $symbol - the function.
2137 # $declaration - the declaration of the function.
2138 #############################################################################
2140 sub OutputFunction {
2141 my ($symbol, $declaration, $symbol_type) = @_;
2142 my $id = &CreateValidSGMLID ($symbol);
2143 my $condition = &MakeConditionDescription ($symbol);
2145 # Take out the return type $1 $2 $3
2146 $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//;
2147 my $type_modifier = defined($1) ? $1 : "";
2150 # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2151 $pointer =~ s/\s+$//;
2152 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2154 #if ($symbol_type eq 'USER_FUNCTION') {
2155 # $start = "typedef ";
2158 # We output const rather than G_CONST_RETURN.
2159 $type_modifier =~ s/G_CONST_RETURN/const/g;
2160 $pointer =~ s/G_CONST_RETURN/const/g;
2161 $pointer =~ s/^\s+/ /g;
2163 my $ret_type_output;
2164 $ret_type_output = "$start$type_modifier$xref$pointer\n";
2167 $indent_len = length ($symbol) + 2;
2168 my $char1 = my $char2 = my $char3 = "";
2169 if ($symbol_type eq 'USER_FUNCTION') {
2171 $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2173 $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2176 my ($symbol_output, $symbol_desc_output);
2177 $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2178 if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2179 $symbol_desc_output = "$char1$char2$symbol$char3 ";
2181 $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2182 $symbol_desc_output = "$char1$char2$symbol$char3\n"
2183 . (' ' x ($indent_len - 1));
2186 my $synop = "<row><entry role=\"function_type\">${ret_type_output}</entry><entry role=\"function_name\">${symbol_output} <phrase role=\"c_punctuation\">()</phrase></entry></row>\n";
2188 my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol} ()</title>\n";
2190 $desc .= MakeIndexterms($symbol, $id);
2192 $desc .= OutputSymbolExtraLinks($symbol);
2194 $desc .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2196 my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2198 &tagify($_[0],"parameter");
2201 for (my $i = 1; $i <= $#fields; $i += 2) {
2202 my $field_name = $fields[$i];
2205 $desc .= "$field_name";
2208 . (' ' x $indent_len)
2214 $desc .= ");</programlisting>\n";
2216 $desc .= &MakeDeprecationNote($symbol);
2218 if (defined ($SymbolDocs{$symbol})) {
2219 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2221 if (defined ($SymbolAnnotations{$symbol})) {
2222 my $param_desc = $SymbolAnnotations{$symbol};
2223 my $param_annotations = "";
2224 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2225 if ($param_annotations ne "") {
2226 $desc .= "\n<para>$param_annotations</para>";
2230 $desc .= &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2231 $desc .= OutputSymbolTraits ($symbol);
2232 $desc .= "</refsect2>\n";
2233 return ($synop, $desc);
2237 #############################################################################
2238 # Function : OutputParamDescriptions
2239 # Description : Returns the DocBook output describing the parameters of a
2240 # function, macro or signal handler.
2241 # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2242 # handlers have an implicit user_data parameter last.
2243 # $symbol - the name of the function/macro being described.
2244 # @fields - parsed fields from the declaration, used to determine
2245 # undocumented/unused entries
2246 #############################################################################
2248 sub OutputParamDescriptions {
2249 my ($symbol_type, $symbol, @fields) = @_;
2251 my $params = $SymbolParams{$symbol};
2253 my %field_descrs = ();
2256 %field_descrs = @fields;
2257 delete $field_descrs{"void"};
2258 delete $field_descrs{"Returns"};
2261 if (defined $params) {
2263 my $params_desc = "";
2264 my $missing_parameters = "";
2265 my $unused_parameters = "";
2268 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2269 my $param_name = $$params[$j];
2270 my $param_desc = $$params[$j + 1];
2271 my $param_annotations = "";
2273 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2274 $param_desc = &ConvertMarkDown($symbol, $param_desc);
2276 $param_desc =~ s/^(\s|\n)+//msg;
2277 $param_desc =~ s/(\s|\n)+$//msg;
2278 if ($param_name eq "Returns") {
2279 $returns = $param_desc;
2280 if ($param_annotations ne "") {
2281 $returns .= "\n<para>$param_annotations</para>";
2283 } elsif ($param_name eq "void") {
2284 # FIXME: &LogWarning()?
2285 @TRACE@("!!!! void in params for $symbol?\n");
2288 if (!defined $field_descrs{$param_name}) {
2289 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2290 "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2291 if ($unused_parameters ne "") {
2292 $unused_parameters .= ", ".$param_name;
2294 $unused_parameters = $param_name;
2297 delete $field_descrs{$param_name};
2300 if($param_desc ne "") {
2301 $params_desc .= "<row><entry role=\"parameter_name\"><para>$param_name</para></entry>\n<entry role=\"parameter_description\">$param_desc</entry>\n<entry role=\"parameter_annotations\">$param_annotations</entry></row>\n";
2306 foreach my $param_name (keys %field_descrs) {
2307 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2308 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2309 if ($missing_parameters ne "") {
2310 $missing_parameters .= ", ".$param_name;
2312 $missing_parameters = $param_name;
2316 # Signals have an implicit user_data parameter which we describe.
2317 if ($symbol_type eq "SIGNAL") {
2318 $params_desc .= "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry role=\"parameter_description\"><simpara>user data set when the signal handler was connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n";
2321 # Start a table if we need one.
2322 if ($params_desc ne "") {
2324 <refsect3 role="parameters">\n<title>Parameters</title>
2325 <informaltable role="parameters_table" pgwide="1" frame="none">
2327 <colspec colname="parameters_name" colwidth="150px"/>
2328 <colspec colname="parameters_description"/>
2329 <colspec colname="parameters_annotations" colwidth="200px"/>
2332 $output .= $params_desc;
2333 $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2336 # Output the returns info last
2337 if ($returns ne "") {
2339 <refsect3 role=\"returns\">\n<title>Returns</title>
2341 $output .= $returns;
2342 $output .= "\n</refsect3>";
2345 # remember missing/unused parameters (needed in tmpl-free build)
2346 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2347 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2349 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2350 $AllUnusedSymbols{$symbol}=$unused_parameters;
2353 if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2354 if (! exists ($AllIncompleteSymbols{$symbol})) {
2355 $AllIncompleteSymbols{$symbol}="<parameters>";
2363 #############################################################################
2364 # Function : ParseStabilityLevel
2365 # Description : Parses a stability level and outputs a warning if it isn't
2367 # Arguments : $stability - the stability text.
2368 # $file, $line - context for error message
2369 # $message - description of where the level is from, to use in
2370 # any error message.
2371 # Returns : The parsed stability level string.
2372 #############################################################################
2374 sub ParseStabilityLevel {
2375 my ($stability, $file, $line, $message) = @_;
2377 $stability =~ s/^\s*//;
2378 $stability =~ s/\s*$//;
2379 if ($stability =~ m/^stable$/i) {
2380 $stability = "Stable";
2381 } elsif ($stability =~ m/^unstable$/i) {
2382 $stability = "Unstable";
2383 } elsif ($stability =~ m/^private$/i) {
2384 $stability = "Private";
2386 &LogWarning ($file, $line, "$message is $stability.".
2387 "It should be one of these: Stable, Unstable, or Private.");
2393 #############################################################################
2394 # Function : OutputDBFile
2395 # Description : Outputs the final DocBook file for one section.
2396 # Arguments : $file - the name of the file.
2397 # $title - the title from the $MODULE-sections.txt file, which
2398 # will be overridden by the title in the template file.
2399 # $section_id - the id to use for the toplevel tag.
2400 # $includes - comma-separates list of include files added at top of
2401 # synopsis, with '<' '>' around them (if not already enclosed in "").
2402 # $functions_synop - reference to the DocBook for the Functions Synopsis part.
2403 # $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2404 # $functions_details - reference to the DocBook for the Functions Details part.
2405 # $other_details - reference to the DocBook for the Types and Values Details part.
2406 # $signal_synop - reference to the DocBook for the Signal Synopsis part
2407 # $signal_desc - reference to the DocBook for the Signal Description part
2408 # $args_synop - reference to the DocBook for the Arg Synopsis part
2409 # $args_desc - reference to the DocBook for the Arg Description part
2410 # $hierarchy - reference to the DocBook for the Object Hierarchy part
2411 # $interfaces - reference to the DocBook for the Interfaces part
2412 # $implementations - reference to the DocBook for the Known Implementations part
2413 # $prerequisites - reference to the DocBook for the Prerequisites part
2414 # $derived - reference to the DocBook for the Derived Interfaces part
2415 # $file_objects - reference to an array of objects in this file
2416 #############################################################################
2419 my ($file, $title, $section_id, $includes, $functions_synop, $other_synop, $functions_details, $other_details, $signals_synop, $signals_desc, $args_synop, $args_desc, $hierarchy, $interfaces, $implementations, $prerequisites, $derived, $file_objects) = @_;
2421 @TRACE@("Output docbook for file $file with title '$title'\n");
2423 # The edited title overrides the one from the sections file.
2424 my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2425 if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2426 $title = $new_title;
2427 @TRACE@("Found title: $title\n");
2429 my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2430 if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2433 # Don't use ConvertMarkDown here for now since we don't want blocks
2434 $short_desc = &ExpandAbbreviations("$title:Short_description",
2436 @TRACE@("Found short_desc: $short_desc");
2438 my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2439 if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2442 $long_desc = &ConvertMarkDown("$title:Long_description",
2444 @TRACE@("Found long_desc: $long_desc");
2446 my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2447 if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2450 $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2451 @TRACE@("Found see_also: $see_also");
2454 $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2456 my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2457 if (!defined ($stability) || $stability =~ m/^\s*$/) {
2460 $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2461 @TRACE@("Found stability: $stability");
2464 $AnnotationsUsed{$stability} = 1;
2465 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2466 } elsif ($DEFAULT_STABILITY) {
2467 $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2468 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2471 my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2472 if (!defined ($image) || $image =~ m/^\s*$/) {
2480 if ($image =~ /jpe?g$/i) {
2481 $format = "format='JPEG'";
2482 } elsif ($image =~ /png$/i) {
2483 $format = "format='PNG'";
2484 } elsif ($image =~ /svg$/i) {
2485 $format = "format='SVG'";
2490 $image = " <inlinegraphic fileref='$image' $format/>\n"
2493 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2495 my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2498 my $include_output = "";
2500 $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2502 foreach $include (split (/,/, $includes)) {
2503 if ($include =~ m/^\".+\"$/) {
2504 $include_output .= "#include ${include}\n";
2507 $include =~ s/^\s+|\s+$//gs;
2508 $include_output .= "#include <${include}>\n";
2511 $include_output .= "</synopsis></refsect1>\n";
2514 my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2516 my $old_db_file = "$DB_OUTPUT_DIR/$file.xml";
2517 my $new_db_file = "$DB_OUTPUT_DIR/$file.xml.new";
2519 open (OUTPUT, ">$new_db_file")
2520 || die "Can't create $new_db_file: $!";
2522 my $object_anchors = "";
2523 foreach my $object (@$file_objects) {
2524 next if ($object eq $section_id);
2525 my $id = CreateValidSGMLID($object);
2526 @TRACE@("Adding anchor for $object\n");
2527 $object_anchors .= "<anchor id=\"$id\"/>";
2530 # Make sure we produce valid docbook
2531 $$functions_details ||= "<para />";
2533 # We used to output this, but is messes up our UpdateFileIfChanged code
2534 # since it changes every day (and it is only used in the man pages):
2535 # "<refentry id="$section_id" revision="$mday $month $year">"
2538 ${\( MakeDocHeader ("refentry") )}
2539 <refentry id="$section_id">
2541 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2542 <manvolnum>3</manvolnum>
2543 <refmiscinfo>\U$MODULE\E Library$image</refmiscinfo>
2546 <refname>$title</refname>
2547 <refpurpose>$short_desc</refpurpose>
2550 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2552 <refsect1 id="$section_id.description" role="desc">
2553 <title role="desc.title">Description</title>
2554 $extralinks$long_desc
2556 <refsect1 id="$section_id.functions_details" role="details">
2557 <title role="details.title">Functions</title>
2560 <refsect1 id="$section_id.other_details" role="details">
2561 <title role="details.title">Types and Values</title>
2564 $$args_desc$$signals_desc$see_also
2569 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2573 #############################################################################
2574 # Function : OutputExtraFile
2575 # Description : Copies an "extra" DocBook file into the output directory,
2576 # expanding abbreviations
2577 # Arguments : $file - the source file.
2578 #############################################################################
2579 sub OutputExtraFile {
2584 ($basename = $file) =~ s!^.*/!!;
2586 my $old_db_file = "$DB_OUTPUT_DIR/$basename";
2587 my $new_db_file = "$DB_OUTPUT_DIR/$basename.new";
2591 open(EXTRA_FILE, "<$file") || die "Can't open $file";
2595 $contents = <EXTRA_FILE>;
2598 open (OUTPUT, ">$new_db_file")
2599 || die "Can't create $new_db_file: $!";
2601 print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2604 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2606 #############################################################################
2607 # Function : OutputBook
2608 # Description : Outputs the entities that need to be included into the
2609 # main docbook file for the module.
2610 # Arguments : $book_top - the declarations of the entities, which are added
2611 # at the top of the main docbook file.
2612 # $book_bottom - the references to the entities, which are
2613 # added in the main docbook file at the desired position.
2614 #############################################################################
2617 my ($book_top, $book_bottom) = @_;
2619 my $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.top";
2620 my $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.top.new";
2622 open (OUTPUT, ">$new_file")
2623 || die "Can't create $new_file: $!";
2624 print OUTPUT $book_top;
2627 &UpdateFileIfChanged ($old_file, $new_file, 0);
2630 $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom";
2631 $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom.new";
2633 open (OUTPUT, ">$new_file")
2634 || die "Can't create $new_file: $!";
2635 print OUTPUT $book_bottom;
2638 &UpdateFileIfChanged ($old_file, $new_file, 0);
2641 # If the main docbook file hasn't been created yet, we create it here.
2642 # The user can tweak it later.
2643 if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2644 open (OUTPUT, ">$MAIN_SGML_FILE")
2645 || die "Can't create $MAIN_SGML_FILE: $!";
2648 ${\( MakeDocHeader ("book") )}
2651 <title>$MODULE Reference Manual</title>
2653 for $MODULE [VERSION].
2654 The latest version of this documentation can be found on-line at
2655 <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2660 <title>[Insert title here]</title>
2664 if (-e $OBJECT_TREE_FILE) {
2666 <chapter id="object-tree">
2667 <title>Object Hierarchy</title>
2668 <xi:include href="xml/tree_index.sgml"/>
2673 <!-- enable this when you use gobject types
2674 <chapter id="object-tree">
2675 <title>Object Hierarchy</title>
2676 <xi:include href="xml/tree_index.sgml"/>
2682 <index id="api-index-full">
2683 <title>API Index</title>
2684 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2686 <index id="deprecated-api-index" role="deprecated">
2687 <title>Index of deprecated API</title>
2688 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2691 if (keys(%AnnotationsUsed)) {
2693 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2697 <!-- enable this when you use gobject introspection annotations
2698 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2711 #############################################################################
2712 # Function : CreateValidSGML
2713 # Description : This turns any chars which are used in SGML into entities,
2714 # e.g. '<' into '<'
2715 # Arguments : $text - the text to turn into proper SGML.
2716 #############################################################################
2718 sub CreateValidSGML {
2720 $text =~ s/&/&/g; # Do this first, or the others get messed up.
2721 $text =~ s/</</g;
2722 $text =~ s/>/>/g;
2723 # browers render single tabs inconsistently
2724 $text =~ s/([^\s])\t([^\s])/$1 $2/g;
2728 #############################################################################
2729 # Function : ConvertSGMLChars
2730 # Description : This is used for text in source code comment blocks, to turn
2731 # chars which are used in SGML into entities, e.g. '<' into
2732 # '<'. Depending on $INLINE_MARKUP_MODE, this is done
2733 # unconditionally or only if the character doesn't seem to be
2734 # part of an SGML construct (tag or entity reference).
2735 # Arguments : $text - the text to turn into proper SGML.
2736 #############################################################################
2738 sub ConvertSGMLChars {
2739 my ($symbol, $text) = @_;
2741 if ($INLINE_MARKUP_MODE) {
2742 # For the XML/SGML mode only convert to entities outside CDATA sections.
2743 return &ModifyXMLElements ($text, $symbol,
2744 "<!\\[CDATA\\[|<programlisting[^>]*>",
2745 \&ConvertSGMLCharsEndTag,
2746 \&ConvertSGMLCharsCallback);
2748 # For the simple non-sgml mode, convert to entities everywhere.
2750 # First, convert freestanding & to &
2751 $text =~ s/&(?![a-zA-Z#]+;)/&/g;
2752 $text =~ s/</</g;
2753 # Allow ">" at beginning of string for blockquote markdown
2754 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2761 sub ConvertSGMLCharsEndTag {
2762 if ($_[0] eq "<!\[CDATA\[") {
2765 return "</programlisting>";
2769 sub ConvertSGMLCharsCallback {
2770 my ($text, $symbol, $tag) = @_;
2772 if ($tag =~ m/^<programlisting/) {
2773 # We can handle <programlisting> specially here.
2774 return &ModifyXMLElements ($text, $symbol,
2776 \&ConvertSGMLCharsEndTag,
2777 \&ConvertSGMLCharsCallback2);
2778 } elsif ($tag eq "") {
2779 # If we're not in CDATA convert to entities.
2780 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2781 $text =~ s/<(?![a-zA-Z\/!])/</g;
2782 # Allow ">" at beginning of string for blockquote markdown
2783 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2785 # Handle "#include <xxxxx>"
2786 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2792 sub ConvertSGMLCharsCallback2 {
2793 my ($text, $symbol, $tag) = @_;
2795 # If we're not in CDATA convert to entities.
2796 # We could handle <programlisting> differently, though I'm not sure it helps.
2798 # replace only if its not a tag
2799 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2800 $text =~ s/<(?![a-zA-Z\/!])/</g;
2801 $text =~ s/(?<![a-zA-Z0-9"'\/-])>/>/g;
2803 # Handle "#include <xxxxx>"
2804 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2810 #############################################################################
2811 # Function : ExpandAnnotation
2812 # Description : This turns annotations into acronym tags.
2813 # Arguments : $symbol - the symbol being documented, for error messages.
2814 # $text - the text to expand.
2815 #############################################################################
2816 sub ExpandAnnotation {
2817 my ($symbol, $param_desc) = @_;
2818 my $param_annotations = "";
2820 # look for annotations at the start of the comment part
2821 # function level annotations don't end with a colon ':'
2822 if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
2827 @annotations = split(/\)\s*\(/,$1);
2828 @TRACE@("annotations for $symbol: '$1'\n");
2829 foreach $annotation (@annotations) {
2830 # need to search for the longest key-match in %AnnotationDefinition
2832 my $match_annotation="";
2834 foreach $annotationdef (keys %AnnotationDefinition) {
2835 if ($annotation =~ m/^$annotationdef/) {
2836 if (length($annotationdef)>$match_length) {
2837 $match_length=length($annotationdef);
2838 $match_annotation=$annotationdef;
2842 my $annotation_extra = "";
2843 if ($match_annotation ne "") {
2844 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2845 $annotation_extra = " $1";
2847 $AnnotationsUsed{$match_annotation} = 1;
2848 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2851 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2852 "unknown annotation \"$annotation\" in documentation for $symbol.");
2853 $param_annotations .= "[$annotation]";
2857 $param_desc =~ m/^(.*?)\.*\s*$/s;
2858 $param_desc = "$1. ";
2860 if ($param_annotations ne "") {
2861 $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2863 return ($param_desc, $param_annotations);
2866 #############################################################################
2867 # Function : ExpandAbbreviations
2868 # Description : This turns the abbreviations function(), macro(), @param,
2869 # %constant, and #symbol into appropriate DocBook markup.
2870 # CDATA sections and <programlisting> parts are skipped.
2871 # Arguments : $symbol - the symbol being documented, for error messages.
2872 # $text - the text to expand.
2873 #############################################################################
2875 sub ExpandAbbreviations {
2876 my ($symbol, $text) = @_;
2878 # Note: This is a fallback and normally done in the markdown parser
2880 # Convert "|[" and "]|" into the start and end of program listing examples.
2881 # Support \[<!-- language="C" --> modifiers
2882 $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2883 $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2884 $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2886 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2888 return &ModifyXMLElements ($text, $symbol,
2889 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2890 \&ExpandAbbreviationsEndTag,
2891 \&ExpandAbbreviationsCallback);
2895 # Returns the end tag (as a regexp) corresponding to the given start tag.
2896 sub ExpandAbbreviationsEndTag {
2897 my ($start_tag) = @_;
2899 if ($start_tag eq "<!\[CDATA\[") {
2901 } elsif ($start_tag eq "<!DOCTYPE") {
2903 } elsif ($start_tag =~ m/<(\w+)/) {
2908 # Called inside or outside each CDATA or <programlisting> section.
2909 sub ExpandAbbreviationsCallback {
2910 my ($text, $symbol, $tag) = @_;
2912 if ($tag =~ m/^<programlisting/) {
2913 # Handle any embedded CDATA sections.
2914 return &ModifyXMLElements ($text, $symbol,
2916 \&ExpandAbbreviationsEndTag,
2917 \&ExpandAbbreviationsCallback2);
2918 } elsif ($tag eq "") {
2919 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2921 # We are outside any CDATA or <programlisting> sections, so we expand
2922 # any gtk-doc abbreviations.
2924 # Convert '@param()'
2925 # FIXME: we could make those also links ($symbol.$2), but that would be less
2926 # useful as the link target is a few lines up or down
2927 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2929 # Convert 'function()' or 'macro()'.
2930 # if there is abc_*_def() we don't want to make a link to _def()
2931 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2932 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2933 # handle #Object.func()
2934 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2936 # Convert '@param', but not '\@param'.
2937 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2938 $text =~ s/\\\@/\@/g;
2940 # Convert '%constant', but not '\%constant'.
2941 # Also allow negative numbers, e.g. %-1.
2942 $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2943 $text =~ s/\\\%/\%/g;
2945 # Convert '#symbol', but not '\#symbol'.
2946 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2953 # This is called inside a <programlisting>
2954 sub ExpandAbbreviationsCallback2 {
2955 my ($text, $symbol, $tag) = @_;
2958 # We are inside a <programlisting> but outside any CDATA sections,
2959 # so we expand any gtk-doc abbreviations.
2960 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2961 # why not just call it
2962 $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2963 } elsif ($tag eq "<![CDATA[") {
2964 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2965 $text = &ReplaceEntities ($text, $symbol);
2972 my ($symbol, $tag) = @_;;
2975 # Check for things like '#include', '#define', and skip them.
2976 if ($PreProcessorDirectives{$symbol}) {
2980 # Get rid of special suffixes ('-struct','-enum').
2981 $text =~ s/-struct$//;
2982 $text =~ s/-enum$//;
2984 # If the symbol is in the form "Object::signal", then change the symbol to
2985 # "Object-signal" and use "signal" as the text.
2986 if ($symbol =~ s/::/-/) {
2990 # If the symbol is in the form "Object:property", then change the symbol to
2991 # "Object--property" and use "property" as the text.
2992 if ($symbol =~ s/:/--/) {
2997 $text = tagify ($text, $tag);
3000 return &MakeXRef($symbol, $text);
3004 #############################################################################
3005 # Function : ModifyXMLElements
3006 # Description : Looks for given XML element tags within the text, and calls
3007 # the callback on pieces of text inside & outside those elements.
3008 # Used for special handling of text inside things like CDATA
3009 # and <programlisting>.
3010 # Arguments : $text - the text.
3011 # $symbol - the symbol currently being documented (only used for
3013 # $start_tag_regexp - the regular expression to match start tags.
3014 # e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3015 # CDATA sections or programlisting elements.
3016 # $end_tag_func - function which is passed the matched start tag
3017 # and should return the appropriate end tag string regexp.
3018 # $callback - callback called with each part of the text. It is
3019 # called with a piece of text, the symbol being
3020 # documented, and the matched start tag or "" if the text
3021 # is outside the XML elements being matched.
3022 #############################################################################
3023 sub ModifyXMLElements {
3024 my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3025 my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3028 while ($text =~ m/$start_tag_regexp/s) {
3029 $before_tag = $`; # Prematch for last successful match string
3030 $start_tag = $&; # Last successful match
3031 $text = $'; # Postmatch for last successful match string
3033 $result .= &$callback ($before_tag, $symbol, "");
3034 $result .= $start_tag;
3036 # get the matching end-tag for current tag
3037 $end_tag_regexp = &$end_tag_func ($start_tag);
3039 if ($text =~ m/$end_tag_regexp/s) {
3044 $result .= &$callback ($before_tag, $symbol, $start_tag);
3045 $result .= $end_tag;
3047 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3048 "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3049 # Just assume it is all inside the tag.
3050 $result .= &$callback ($text, $symbol, $start_tag);
3055 # Handle any remaining text outside the tags.
3056 $result .= &$callback ($text, $symbol, "");
3065 # Adds a tag around some text.
3066 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3068 my ($text, $elem) = @_;
3069 return "<" . $elem . ">" . $text . "</" . $elem . ">";
3072 #############################################################################
3073 # Function : MakeDocHeader
3074 # Description : Builds a docbook header for the given tag
3075 # Arguments : $tag - doctype tag
3076 #############################################################################
3080 my $header = $doctype_header;
3081 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE $tag/;
3086 #############################################################################
3087 # Function : MakeXRef
3088 # Description : This returns a cross-reference link to the given symbol.
3089 # Though it doesn't try to do this for a few standard C types
3090 # that it knows won't be in the documentation.
3091 # Arguments : $symbol - the symbol to try to create a XRef to.
3092 # $text - text text to put inside the XRef, defaults to $symbol
3093 #############################################################################
3096 my ($symbol, $text) = ($_[0], $_[1]);
3098 $symbol =~ s/^\s+//;
3099 $symbol =~ s/\s+$//;
3101 if (!defined($text)) {
3104 # Get rid of special suffixes ('-struct','-enum').
3105 $text =~ s/-struct$//;
3106 $text =~ s/-enum$//;
3109 if ($symbol =~ m/ /) {
3113 @TRACE@("Getting type link for $symbol -> $text\n");
3115 my $symbol_id = &CreateValidSGMLID ($symbol);
3116 return "<link linkend=\"$symbol_id\">$text</link>";
3120 #############################################################################
3121 # Function : MakeIndexterms
3122 # Description : This returns a indexterm elements for the given symbol
3123 # Arguments : $symbol - the symbol to create indexterms for
3124 #############################################################################
3126 sub MakeIndexterms {
3127 my ($symbol, $id) = @_;
3131 # make the index useful, by ommiting the namespace when sorting
3132 if ($NAME_SPACE ne "") {
3133 if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3134 $sortas=" sortas=\"$1\"";
3138 if (exists $Deprecated{$symbol}) {
3139 $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3140 $IndexEntriesDeprecated{$symbol}=$id;
3141 $IndexEntriesFull{$symbol}=$id;
3143 if (exists $Since{$symbol}) {
3144 my $since = $Since{$symbol};
3148 $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3150 $IndexEntriesSince{$symbol}=$id;
3151 $IndexEntriesFull{$symbol}=$id;
3154 $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3155 $IndexEntriesFull{$symbol}=$id;
3161 #############################################################################
3162 # Function : MakeDeprecationNote
3163 # Description : This returns a deprecation warning for the given symbol.
3164 # Arguments : $symbol - the symbol to try to create a warning for.
3165 #############################################################################
3167 sub MakeDeprecationNote {
3168 my ($symbol) = $_[0];
3170 if (exists $Deprecated{$symbol}) {
3173 $desc .= "<warning><para><literal>$symbol</literal> ";
3175 $note = $Deprecated{$symbol};
3177 if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3178 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3180 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3182 $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3186 $note = &ConvertMarkDown($symbol, $note);
3187 $desc .= " " . $note;
3189 $desc .= "</warning>\n";
3194 #############################################################################
3195 # Function : MakeConditionDescription
3196 # Description : This returns a sumary of conditions for the given symbol.
3197 # Arguments : $symbol - the symbol to try to create the sumary.
3198 #############################################################################
3200 sub MakeConditionDescription {
3201 my ($symbol) = $_[0];
3204 if (exists $Deprecated{$symbol}) {
3209 if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3210 $desc .= "deprecated:$1";
3212 $desc .= "deprecated";
3216 if (exists $Since{$symbol}) {
3221 if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3222 $desc .= "since:$1";
3228 if (exists $StabilityLevel{$symbol}) {
3232 $desc .= "stability:".$StabilityLevel{$symbol};
3237 $cond =~ s/\"/"/g;
3238 $desc=" condition=\"".$cond."\"";
3239 @TRACE@("condition for '$symbol' = '$desc'\n");
3244 #############################################################################
3245 # Function : GetHierarchy
3246 # Description : Returns the DocBook output describing the ancestors and
3247 # immediate children of a GObject subclass. It uses the
3248 # global @Objects and @ObjectLevels arrays to walk the tree.
3250 # Arguments : $object - the GtkObject subclass.
3251 # @hierarchy - previous hierarchy
3252 #############################################################################
3255 my ($object,$hierarchy_ref) = @_;
3256 my @hierarchy = @{$hierarchy_ref};
3258 # Find object in the objects array.
3264 for ($i = 0; $i < @Objects; $i++) {
3266 if ($ObjectLevels[$i] <= $level) {
3269 elsif ($ObjectLevels[$i] == $level + 1) {
3270 push (@children, $Objects[$i]);
3273 elsif ($Objects[$i] eq $object) {
3276 $level = $ObjectLevels[$i];
3283 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3285 push (@ancestors, $object);
3286 @TRACE@("Level: $level\n");
3287 while ($level > 1) {
3289 if ($ObjectLevels[$j] < $level) {
3290 push (@ancestors, $Objects[$j]);
3291 $level = $ObjectLevels[$j];
3292 @TRACE@("Level: $level\n");
3296 # Output the ancestors, indented and with links.
3299 for ($i = $#ancestors; $i >= 0; $i--) {
3302 my $ancestor = $ancestors[$i];
3303 my $ancestor_id = &CreateValidSGMLID ($ancestor);
3304 my $indent = ' ' x ($level * 4);
3305 # Don't add a link to the current object, i.e. when i == 0.
3307 $entry_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3308 $alt_text = $indent . $ancestor;
3310 $entry_text = $indent . $ancestor;
3311 $alt_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3313 @TRACE@("Checking for '$entry_text' or '$alt_text'");
3314 # Check if we already have this object
3316 for ($j = 0; $j <= $#hierarchy; $j++) {
3317 if (($hierarchy[$j] eq $entry_text) or ($hierarchy[$j] eq $alt_text)) {
3323 # We have a new entry, find insert position in alphabetical order
3325 for ($j = $last_index; $j <= $#hierarchy; $j++) {
3326 if ($hierarchy[$j] !~ m/^${indent}/) {
3330 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3331 my $stripped_text = $hierarchy[$j];
3332 if ($entry_text !~ m/<link linkend/) {
3333 $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3334 $stripped_text =~ s%</link>%%;
3336 if ($entry_text lt $stripped_text) {
3345 $last_index = 1 + $#hierarchy;
3347 splice @hierarchy, $last_index, 0, ($entry_text);
3350 # Already have this one, make sure we use the not linked version
3351 if ($entry_text !~ m/<link linkend=/) {
3352 $hierarchy[$j] = $entry_text;
3354 # Remember index as base insert point
3355 $last_index = $index + 1;
3359 # Output the children, indented and with links.
3360 for ($i = 0; $i <= $#children; $i++) {
3361 my $id = &CreateValidSGMLID ($children[$i]);
3362 my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3363 splice @hierarchy, $last_index, 0, ($indented_text);
3370 #############################################################################
3371 # Function : GetInterfaces
3372 # Description : Returns the DocBook output describing the interfaces
3373 # implemented by a class. It uses the global %Interfaces hash.
3374 # Arguments : $object - the GtkObject subclass.
3375 #############################################################################
3382 # Find object in the objects array.
3383 if (exists($Interfaces{$object})) {
3384 my @ifaces = split(' ', $Interfaces{$object});
3389 for ($i = 0; $i <= $#ifaces; $i++) {
3390 my $id = &CreateValidSGMLID ($ifaces[$i]);
3391 $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3392 if ($i < $#ifaces - 1) {
3395 elsif ($i < $#ifaces) {
3410 #############################################################################
3411 # Function : GetImplementations
3412 # Description : Returns the DocBook output describing the implementations
3413 # of an interface. It uses the global %Interfaces hash.
3414 # Arguments : $object - the GtkObject subclass.
3415 #############################################################################
3417 sub GetImplementations {
3422 foreach my $key (keys %Interfaces) {
3423 if ($Interfaces{$key} =~ /\b$object\b/) {
3424 push (@impls, $key);
3428 @impls = sort @impls;
3431 $object is implemented by
3433 for ($i = 0; $i <= $#impls; $i++) {
3434 my $id = &CreateValidSGMLID ($impls[$i]);
3435 $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3436 if ($i < $#impls - 1) {
3439 elsif ($i < $#impls) {
3454 #############################################################################
3455 # Function : GetPrerequisites
3456 # Description : Returns the DocBook output describing the prerequisites
3457 # of an interface. It uses the global %Prerequisites hash.
3458 # Arguments : $iface - the interface.
3459 #############################################################################
3461 sub GetPrerequisites {
3466 if (exists($Prerequisites{$iface})) {
3471 my @prereqs = split(' ', $Prerequisites{$iface});
3472 for ($i = 0; $i <= $#prereqs; $i++) {
3473 my $id = &CreateValidSGMLID ($prereqs[$i]);
3474 $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3475 if ($i < $#prereqs - 1) {
3478 elsif ($i < $#prereqs) {
3492 #############################################################################
3493 # Function : GetDerived
3494 # Description : Returns the DocBook output describing the derived interfaces
3495 # of an interface. It uses the global %Prerequisites hash.
3496 # Arguments : $iface - the interface.
3497 #############################################################################
3505 foreach my $key (keys %Prerequisites) {
3506 if ($Prerequisites{$key} =~ /\b$iface\b/) {
3507 push (@derived, $key);
3510 if ($#derived >= 0) {
3511 @derived = sort @derived;
3514 $iface is required by
3516 for ($i = 0; $i <= $#derived; $i++) {
3517 my $id = &CreateValidSGMLID ($derived[$i]);
3518 $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3519 if ($i < $#derived - 1) {
3522 elsif ($i < $#derived) {
3537 #############################################################################
3538 # Function : GetSignals
3539 # Description : Returns the synopsis and detailed description DocBook output
3540 # for the signal handlers of a given GtkObject subclass.
3541 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3542 #############################################################################
3550 for ($i = 0; $i <= $#SignalObjects; $i++) {
3551 if ($SignalObjects[$i] eq $object) {
3552 @TRACE@("Found signal: $SignalNames[$i]\n");
3553 my $name = $SignalNames[$i];
3554 my $symbol = "${object}::${name}";
3555 my $id = &CreateValidSGMLID ("$object-$name");
3557 $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3558 $desc .= MakeIndexterms($symbol, $id);
3560 $desc .= OutputSymbolExtraLinks($symbol);
3562 $desc .= "<programlisting language=\"C\">";
3564 $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3565 my $type_modifier = defined($1) ? $1 : "";
3568 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3570 my $ret_type_output = "$type_modifier$xref$pointer";
3571 my $callback_name = "user_function";
3572 $desc .= "${ret_type_output}\n${callback_name} (";
3574 my $indentation = ' ' x (length($callback_name) + 2);
3575 my $pad = $indentation;
3577 my $sourceparams = $SourceSymbolParams{$symbol};
3578 my @params = split ("\n", $SignalPrototypes[$i]);
3581 my $type_len = length("gpointer");
3582 my $name_len = length("user_data");
3583 # do two passes, the first one is to calculate padding
3584 for ($l = 0; $l < 2; $l++) {
3585 for ($j = 0; $j <= $#params; $j++) {
3587 # allow alphanumerics, '_', '[' & ']' in param names
3588 if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3591 if (defined($sourceparams)) {
3592 $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3597 if (!defined($param_name)) {
3598 $param_name = "arg$j";
3601 if (length($type) + length($pointer) > $type_len) {
3602 $type_len = length($type) + length($pointer);
3604 if (length($param_name) > $name_len) {
3605 $name_len = length($param_name);
3609 $xref = &MakeXRef ($type, &tagify($type, "type"));
3610 $pad = ' ' x ($type_len - length($type) - length($pointer));
3611 $desc .= "$xref$pad $pointer${param_name},\n";
3612 $desc .= $indentation;
3615 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3616 "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3620 $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3621 $pad = ' ' x ($type_len - length("gpointer"));
3622 $desc .= "$xref$pad user_data)";
3623 $desc .= "</programlisting>\n";
3625 my $flags = $SignalFlags[$i];
3626 my $flags_string = "";
3628 if (defined ($flags)) {
3629 if ($flags =~ m/f/) {
3630 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3632 elsif ($flags =~ m/l/) {
3633 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3635 elsif ($flags =~ m/c/) {
3636 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3637 $flags_string = "Cleanup";
3639 if ($flags =~ m/r/) {
3640 if ($flags_string) { $flags_string .= " / "; }
3641 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3643 if ($flags =~ m/d/) {
3644 if ($flags_string) { $flags_string .= " / "; }
3645 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3647 if ($flags =~ m/a/) {
3648 if ($flags_string) { $flags_string .= " / "; }
3649 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3651 if ($flags =~ m/h/) {
3652 if ($flags_string) { $flags_string .= " / "; }
3653 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3657 $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";
3659 my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3661 $AllSymbols{$symbol} = 1;
3662 if (defined ($SymbolDocs{$symbol})) {
3663 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3665 $desc .= $symbol_docs;
3667 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3668 $AllDocumentedSymbols{$symbol} = 1;
3671 if (defined ($SymbolAnnotations{$symbol})) {
3672 my $param_desc = $SymbolAnnotations{$symbol};
3673 my $param_annotations = "";
3674 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3675 if ($param_annotations ne "") {
3676 $desc .= "\n<para>$param_annotations</para>";
3679 $desc .= &MakeDeprecationNote($symbol);
3681 $desc .= $parameters;
3682 if ($flags_string) {
3683 $desc .= "<para>Flags: $flags_string</para>\n";
3685 $desc .= OutputSymbolTraits ($symbol);
3686 $desc .= "</refsect2>";
3689 return ($synop, $desc);
3693 #############################################################################
3694 # Function : GetArgs
3695 # Description : Returns the synopsis and detailed description DocBook output
3696 # for the Args of a given GtkObject subclass.
3697 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3698 #############################################################################
3704 my $child_synop = "";
3705 my $child_desc = "";
3706 my $style_synop = "";
3707 my $style_desc = "";
3710 for ($i = 0; $i <= $#ArgObjects; $i++) {
3711 if ($ArgObjects[$i] eq $object) {
3712 @TRACE@("Found arg: $ArgNames[$i]\n");
3713 my $name = $ArgNames[$i];
3714 my $flags = $ArgFlags[$i];
3715 my $flags_string = "";
3719 if ($flags =~ m/c/) {
3720 $kind = "child property";
3723 elsif ($flags =~ m/s/) {
3724 $kind = "style property";
3731 # Remember only one colon so we don't clash with signals.
3732 my $symbol = "${object}:${name}";
3733 # use two dashes and ev. an extra separator here for the same reason.
3734 my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3736 my $type = $ArgTypes[$i];
3738 my $range = $ArgRanges[$i];
3739 my $range_output = CreateValidSGML ($range);
3740 my $default = $ArgDefaults[$i];
3741 my $default_output = CreateValidSGML ($default);
3743 if ($type eq "GtkString") {
3744 $type = "char *";
3746 if ($type eq "GtkSignal") {
3747 $type = "GtkSignalFunc, gpointer";
3748 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3749 . &MakeXRef ("gpointer");
3750 } elsif ($type =~ m/^(\w+)\*$/) {
3751 $type_output = &MakeXRef ($1, &tagify($1, "type")) . " *";
3753 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3756 if ($flags =~ m/r/) {
3757 $flags_string = "Read";
3759 if ($flags =~ m/w/) {
3760 if ($flags_string) { $flags_string .= " / "; }
3761 $flags_string .= "Write";
3763 if ($flags =~ m/x/) {
3764 if ($flags_string) { $flags_string .= " / "; }
3765 $flags_string .= "Construct";
3767 if ($flags =~ m/X/) {
3768 if ($flags_string) { $flags_string .= " / "; }
3769 $flags_string .= "Construct Only";
3772 $AllSymbols{$symbol} = 1;
3774 if (defined($SymbolDocs{$symbol}) &&
3775 !IsEmptyDoc($SymbolDocs{$symbol})) {
3776 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3777 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3778 $AllDocumentedSymbols{$symbol} = 1;
3781 if ($ArgBlurbs[$i] ne "") {
3782 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3783 $AllDocumentedSymbols{$symbol} = 1;
3785 # FIXME: print a warning?
3786 @TRACE@(".. no description\n");
3790 my $pad1 = " " x (24 - length ($name));
3792 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";
3793 my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3794 $arg_desc .= MakeIndexterms($symbol, $id);
3796 $arg_desc .= OutputSymbolExtraLinks($symbol);
3798 $arg_desc .= "<programlisting> “$name”$pad1 $type_output</programlisting>\n";
3799 $arg_desc .= $blurb;
3800 if (defined ($SymbolAnnotations{$symbol})) {
3801 my $param_desc = $SymbolAnnotations{$symbol};
3802 my $param_annotations = "";
3803 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3804 if ($param_annotations ne "") {
3805 $arg_desc .= "\n<para>$param_annotations</para>";
3808 $arg_desc .= &MakeDeprecationNote($symbol);
3810 if ($flags_string) {
3811 $arg_desc .= "<para>Flags: $flags_string</para>\n";
3814 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3816 if ($default ne "") {
3817 $arg_desc .= "<para>Default value: $default_output</para>\n";
3819 $arg_desc .= OutputSymbolTraits ($symbol);
3820 $arg_desc .= "</refsect2>\n";
3822 if ($flags =~ m/c/) {
3823 $child_synop .= $arg_synop;
3824 $child_desc .= $arg_desc;
3826 elsif ($flags =~ m/s/) {
3827 $style_synop .= $arg_synop;
3828 $style_desc .= $arg_desc;
3831 $synop .= $arg_synop;
3836 return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3840 #############################################################################
3841 # Function : ReadSourceDocumentation
3842 # Description : This reads in the documentation embedded in comment blocks
3843 # in the source code (for Gnome).
3845 # Parameter descriptions override any in the template files.
3846 # Function descriptions are placed before any description from
3847 # the template files.
3849 # It recursively descends the source directory looking for .c
3850 # files and scans them looking for specially-formatted comment
3853 # Arguments : $source_dir - the directory to scan.
3854 #############m###############################################################
3856 sub ReadSourceDocumentation {
3857 my ($source_dir) = @_;
3858 my ($file, $dir, @suffix_list, $suffix);
3860 # prepend entries from @SOURCE_DIR
3861 for my $dir (@SOURCE_DIRS) {
3862 # Check if the filename is in the ignore list.
3863 if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3864 @TRACE@("Skipping source directory: $source_dir");
3867 @TRACE@("No match for: ".($1 || $source_dir));
3871 @TRACE@("Scanning source directory: $source_dir");
3873 # This array holds any subdirectories found.
3876 @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3878 opendir (SRCDIR, $source_dir)
3879 || die "Can't open source directory $source_dir: $!";
3881 foreach $file (readdir (SRCDIR)) {
3882 if ($file =~ /^\./) {
3884 } elsif (-d "$source_dir/$file") {
3885 push (@subdirs, $file);
3886 } elsif (@suffix_list) {
3887 foreach $suffix (@suffix_list) {
3888 if ($file =~ m/\.\Q${suffix}\E$/) {
3889 &ScanSourceFile ("$source_dir/$file");
3892 } elsif ($file =~ m/\.[ch]$/) {
3893 &ScanSourceFile ("$source_dir/$file");
3898 # Now recursively scan the subdirectories.
3899 foreach $dir (@subdirs) {
3900 &ReadSourceDocumentation ("$source_dir/$dir");
3905 #############################################################################
3906 # Function : ScanSourceFile
3907 # Description : Scans one source file looking for specially-formatted comment
3908 # blocks. Later &MergeSourceDocumentation is used to merge any
3909 # documentation found with the documentation already read in
3910 # from the template files.
3912 # Arguments : $file - the file to scan.
3913 #############################################################################
3915 sub ScanSourceFile {
3919 # prepend entries from @SOURCE_DIR
3920 for my $dir (@SOURCE_DIRS) {
3921 # Check if the filename is in the ignore list.
3922 if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3923 @TRACE@("Skipping source file: $file");
3928 if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3931 &LogWarning ($file, 1, "Can't find basename for this filename.");
3935 # Check if the basename is in the list of files to ignore.
3936 if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3937 @TRACE@("Skipping source file: $file");
3941 @TRACE@("Scanning source file: $file");
3943 open (SRCFILE, $file)
3944 || die "Can't open $file: $!";
3945 my $in_comment_block = 0;
3948 my ($description, $return_desc);
3949 my ($since_desc, $stability_desc, $deprecated_desc);
3953 # Look for the start of a comment block.
3954 if (!$in_comment_block) {
3955 if (m%^\s*/\*.*\*/%) {
3956 #one-line comment - not gtkdoc
3957 } elsif (m%^\s*/\*\*\s%) {
3958 @TRACE@("Found comment block start\n");
3960 $in_comment_block = 1;
3962 # Reset all the symbol data.
3968 $deprecated_desc = "";
3969 $stability_desc = "";
3970 $current_param = -1;
3976 # We're in a comment block. Check if we've found the end of it.
3979 # maybe its not even meant to be a gtk-doc comment?
3980 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3982 # Add the return value description onto the end of the params.
3984 # TODO(ensonic): check for duplicated Return docs
3985 # &LogWarning ($file, $., "Multiple Returns for $symbol.");
3986 push (@params, "Returns");
3987 push (@params, $return_desc);
3989 # Convert special characters
3990 $description = &ConvertSGMLChars ($symbol, $description);
3992 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3993 $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3996 # Handle Section docs
3997 if ($symbol =~ m/SECTION:\s*(.*)/) {
4001 if (scalar %KnownSymbols) {
4002 if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
4003 &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
4007 @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
4008 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4009 @TRACE@(" '".$params[$k]."'\n");
4010 $params[$k] = "\L$params[$k]";
4012 if ($params[$k] eq "short_description") {
4013 $key = "$TMPL_DIR/$real_symbol:Short_Description";
4014 } elsif ($params[$k] eq "see_also") {
4015 $key = "$TMPL_DIR/$real_symbol:See_Also";
4016 } elsif ($params[$k] eq "title") {
4017 $key = "$TMPL_DIR/$real_symbol:Title";
4018 } elsif ($params[$k] eq "stability") {
4019 $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4020 } elsif ($params[$k] eq "section_id") {
4021 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4022 } elsif ($params[$k] eq "include") {
4023 $key = "$TMPL_DIR/$real_symbol:Include";
4024 } elsif ($params[$k] eq "image") {
4025 $key = "$TMPL_DIR/$real_symbol:Image";
4027 if (defined($key)) {
4028 $SourceSymbolDocs{$key}=$params[$k+1];
4029 $SourceSymbolSourceFile{$key} = $file;
4030 $SourceSymbolSourceLine{$key} = $.;
4033 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4034 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4035 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4036 #$SourceSymbolTypes{$symbol} = "SECTION";
4038 @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4039 $SourceSymbolDocs{$symbol} = $description;
4040 $SourceSymbolParams{$symbol} = [ @params ];
4041 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4042 #if (defined $DeclarationTypes{$symbol}) {
4043 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4045 $SourceSymbolSourceFile{$symbol} = $file;
4046 $SourceSymbolSourceLine{$symbol} = $.;
4050 ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4051 $since_desc =~ s/^\s+//;
4052 $since_desc =~ s/\s+$//;
4053 @TRACE@("Since($symbol) : [$since_desc]\n");
4054 $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4055 if(scalar @extra_lines) {
4056 &LogWarning ($file, $., "multi-line since docs found");
4060 if ($stability_desc) {
4061 $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4062 $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4065 if ($deprecated_desc) {
4066 if (!exists $Deprecated{$symbol}) {
4067 # don't warn for signals and properties
4068 #if ($symbol !~ m/::?(.*)/) {
4069 if (defined $DeclarationTypes{$symbol}) {
4070 &LogWarning ($file, $.,
4071 "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4072 " (See the --deprecated-guards option for gtkdoc-scan.)");
4075 $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4079 $in_comment_block = 0;
4083 # Get rid of ' * ' at start of every line in the comment block.
4085 # But make sure we don't get rid of the newline at the end.
4089 @TRACE@("scanning :$_");
4091 # If we haven't found the symbol name yet, look for it.
4093 if (m%^\s*(SECTION:\s*\S+)%) {
4095 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4096 } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._() ]+?\)\s*)*$%) {
4098 my $annotation = $2;
4099 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4100 if (defined($annotation)) {
4102 if ($annotation ne "") {
4103 $SymbolAnnotations{$symbol} = $annotation;
4104 @TRACE@("remaining text for $symbol: '$annotation'\n");
4111 if ($in_part eq "description") {
4112 # Get rid of 'Description:'
4113 s%^\s*Description:%%;
4116 if (m%^\s*(returns|return\s+value):%i) {
4117 # we're in param section and have not seen the blank line
4118 if($in_part ne "") {
4120 $in_part = "return";
4123 } elsif (m%^\s*since:%i) {
4124 # we're in param section and have not seen the blank line
4125 if($in_part ne "param") {
4130 } elsif (m%^\s*deprecated:%i) {
4131 # we're in param section and have not seen the blank line
4132 if($in_part ne "param") {
4133 $deprecated_desc = $';
4134 $in_part = "deprecated";
4137 } elsif (m%^\s*stability:%i) {
4138 $stability_desc = $';
4139 $in_part = "stability";
4143 if ($in_part eq "description") {
4146 } elsif ($in_part eq "return") {
4149 } elsif ($in_part eq "since") {
4152 } elsif ($in_part eq "stability") {
4153 $stability_desc .= $_;
4155 } elsif ($in_part eq "deprecated") {
4156 $deprecated_desc .= $_;
4160 # We must be in the parameters. Check for the empty line below them.
4162 $in_part = "description";
4166 # Look for a parameter name.
4167 if (m%^\s*@(\S+)\s*:\s*%) {
4168 my $param_name = $1;
4169 my $param_desc = $';
4171 @TRACE@("Found parameter: $param_name\n");
4172 # Allow varargs variations
4173 if ($param_name =~ m/^\.\.\.$/) {
4174 $param_name = "...";
4176 @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4178 push (@params, $param_name);
4179 push (@params, $param_desc);
4180 $current_param += $PARAM_FIELD_COUNT;
4183 } elsif ($in_part eq "") {
4184 @TRACE@("continuation for $symbol annotation '$_'");
4185 my $annotation = $_;
4186 $annotation =~ s/^\s+|\s+$//g ;
4187 $SymbolAnnotations{$symbol} .= $annotation;
4191 # We must be in the middle of a parameter description, so add it on
4192 # to the last element in @params.
4193 if ($current_param == -1) {
4194 &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
4196 $params[$#params] .= $_;
4202 #############################################################################
4203 # Function : OutputMissingDocumentation
4204 # Description : Outputs report of documentation coverage to a file
4207 #############################################################################
4209 sub OutputMissingDocumentation {
4210 my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4211 my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4213 my $n_documented = 0;
4214 my $n_incomplete = 0;
4220 my $buffer_deprecated = "";
4221 my $buffer_descriptions = "";
4223 open(UNDOCUMENTED, ">$new_undocumented_file")
4224 || die "Can't create $new_undocumented_file";
4226 foreach $symbol (sort (keys (%AllSymbols))) {
4227 # FIXME: should we print LogWarnings for undocumented stuff?
4229 #my $ssfile = &GetSymbolSourceFile($symbol);
4230 #my $ssline = &GetSymbolSourceLine($symbol);
4231 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4233 if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4235 if (exists ($AllDocumentedSymbols{$symbol})) {
4237 if (exists ($AllIncompleteSymbols{$symbol})) {
4239 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4240 #$buffer .= "\t0: ".$location;
4242 } elsif (exists $Deprecated{$symbol}) {
4243 if (exists ($AllIncompleteSymbols{$symbol})) {
4245 $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4246 #$buffer .= "\t1a: ".$location;
4248 $buffer_deprecated .= $symbol . "\n";
4249 #$buffer .= "\t1b: ".$location;
4252 if (exists ($AllIncompleteSymbols{$symbol})) {
4254 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4255 #$buffer .= "\t2a: ".$location;
4257 $buffer .= $symbol . "\n";
4258 #$buffer .= "\t2b: ".$location;
4261 } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4263 if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4264 || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4267 # cut off the leading namespace ($TMPL_DIR)
4268 $symbol =~ m/^.*\/(.*)$/;
4269 $buffer_descriptions .= $1 . "\n";
4274 $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
4279 $percent = ($n_documented / $total) * 100.0;
4282 printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4283 print UNDOCUMENTED "$n_documented symbols documented.\n";
4284 print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4285 print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
4287 print UNDOCUMENTED $buffer;
4288 close (UNDOCUMENTED);
4290 return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4292 printf "%.0f%% symbol docs coverage", $percent;
4293 print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4294 print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4298 #############################################################################
4299 # Function : OutputUndeclaredSymbols
4300 # Description : Outputs symbols that are listed in the section file, but not
4301 # declaration is found in the sources
4304 #############################################################################
4306 sub OutputUndeclaredSymbols {
4307 my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4308 my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4310 open(UNDECLARED, ">$new_undeclared_file")
4311 || die "Can't create $new_undeclared_file";
4313 if (%UndeclaredSymbols) {
4314 print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4315 print UNDECLARED "\n";
4316 print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4320 return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4323 #############################################################################
4324 # Function : OutputUnusedSymbols
4325 # Description : Outputs symbols that are documented in comments, but not
4326 # declared in the sources
4329 #############################################################################
4331 sub OutputUnusedSymbols {
4333 my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4334 my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4336 open (UNUSED, ">$new_unused_file")
4337 || die "Can't open $new_unused_file";
4339 foreach $symbol (sort keys (%Declarations)) {
4340 if (!defined ($DeclarationOutput{$symbol})) {
4341 print (UNUSED "$symbol\n");
4345 foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4346 print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4350 if ($num_unused != 0) {
4351 &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4352 "They should be added to $MODULE-sections.txt in the appropriate place.");
4355 return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4359 #############################################################################
4360 # Function : OutputAllSymbols
4361 # Description : Outputs list of all symbols to a file
4364 #############################################################################
4366 sub OutputAllSymbols {
4367 my $n_documented = 0;
4373 open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4374 || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4376 foreach $symbol (sort (keys (%AllSymbols))) {
4377 print SYMBOLS $symbol . "\n";
4383 #############################################################################
4384 # Function : OutputSymbolsWithoutSince
4385 # Description : Outputs list of all symbols without a since tag to a file
4388 #############################################################################
4390 sub OutputSymbolsWithoutSince {
4391 my $n_documented = 0;
4397 open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4398 || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4400 foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4401 if (!defined $Since{$symbol}) {
4402 print SYMBOLS $symbol . "\n";
4410 #############################################################################
4411 # Function : MergeSourceDocumentation
4412 # Description : This merges documentation read from a source file into the
4413 # documentation read in from a template file.
4415 # Parameter descriptions override any in the template files.
4416 # Function descriptions are placed before any description from
4417 # the template files.
4420 #############################################################################
4422 sub MergeSourceDocumentation {
4426 if (scalar %SymbolDocs) {
4427 @Symbols=keys (%SymbolDocs);
4428 @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4431 # filter scanned declarations, with what we suppress from -sections.txt
4433 foreach $symbol (keys (%Declarations)) {
4434 if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4438 # , add the rest from -sections.txt
4439 foreach $symbol (keys (%KnownSymbols)) {
4440 if ($KnownSymbols{$symbol} == 1) {
4444 # and add whats found in the source
4445 foreach $symbol (keys (%SourceSymbolDocs)) {
4448 @Symbols = keys (%tmp);
4449 @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4451 foreach $symbol (@Symbols) {
4452 $AllSymbols{$symbol} = 1;
4454 my $have_tmpl_docs = 0;
4456 ## see if the symbol is documented in template
4457 my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4458 my $check_tmpl_doc =$tmpl_doc;
4459 # remove all xml-tags and whitespaces
4460 $check_tmpl_doc =~ s/<.*?>//g;
4461 $check_tmpl_doc =~ s/\s//g;
4463 if ($check_tmpl_doc ne "") {
4464 $have_tmpl_docs = 1;
4466 # if the docs have just an empty para, don't merge that.
4467 $check_tmpl_doc = $tmpl_doc;
4468 $check_tmpl_doc =~ s/(\s|\n)//msg;
4469 if ($check_tmpl_doc eq "<para></para>") {
4474 if (exists ($SourceSymbolDocs{$symbol})) {
4475 my $type = $DeclarationTypes {$symbol};
4477 @TRACE@("merging [$symbol] from source\n");
4479 my $item = "Parameter";
4480 if (defined ($type)) {
4481 if ($type eq 'STRUCT') {
4483 } elsif ($type eq 'ENUM') {
4485 } elsif ($type eq 'UNION') {
4492 my $src_doc = $SourceSymbolDocs{$symbol};
4493 # remove leading and training whitespaces
4494 $src_doc =~ s/^\s+//;
4495 $src_doc =~ s/\s+$//;
4497 # Don't output warnings for overridden titles as titles are
4498 # automatically generated in the -sections.txt file, and thus they
4499 # are often overridden.
4500 if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4501 # check if content is different
4502 if ($tmpl_doc ne $src_doc) {
4503 #print "[$tmpl_doc] [$src_doc]\n";
4504 &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4505 "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4509 if ($src_doc ne "") {
4510 $AllDocumentedSymbols{$symbol} = 1;
4513 # Do not add <para> to nothing, it breaks missing docs checks.
4514 my $src_doc_para = "";
4515 if ($src_doc ne "") {
4516 $src_doc_para = $src_doc;
4519 if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4520 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4521 } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4522 # For the title/summary/see also section docs we don't want to
4523 # add any <para> tags.
4524 $SymbolDocs{$symbol} = "$src_doc"
4526 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4530 if ($symbol =~ m/.*::.*/) {
4531 # For signals we prefer the param names from the source docs,
4532 # since the ones from the templates are likely to contain the
4533 # artificial argn names which are generated by gtkdoc-scangobj.
4534 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4535 # FIXME: we need to check for empty docs here as well!
4537 # The templates contain the definitive parameter names and order,
4538 # so we will not change that. We only override the actual text.
4539 my $tmpl_params = $SymbolParams{$symbol};
4540 if (!defined ($tmpl_params)) {
4541 @TRACE@("No merge needed for $symbol\n");
4542 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4543 # FIXME: we still like to get the number of params and merge
4544 # 1) we would noticed that params have been removed/renamed
4545 # 2) we would catch undocumented params
4546 # params are not (yet) exported in -decl.txt so that we
4547 # could easily grab them :/
4549 my $params = $SourceSymbolParams{$symbol};
4551 @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4552 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4553 my $tmpl_param_name = $$tmpl_params[$j];
4555 # Try to find the param in the source comment documentation.
4558 @TRACE@(" try merge param $tmpl_param_name\n");
4559 for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4560 my $param_name = $$params[$k];
4561 my $param_desc = $$params[$k + 1];
4563 @TRACE@(" test param $param_name\n");
4564 # We accept changes in case, since the Gnome source
4565 # docs contain a lot of these.
4566 if ("\L$param_name" eq "\L$tmpl_param_name") {
4569 # Override the description.
4570 $$tmpl_params[$j + 1] = $param_desc;
4572 # Set the name to "" to mark it as used.
4578 # If it looks like the parameters are there, but not
4579 # in the right place, try to explain a bit better.
4580 if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4581 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4582 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4586 # Now we output a warning if parameters have been described which
4588 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4589 my $param_name = $$params[$j];
4591 # the template builder cannot detect if a macro returns
4593 if(($type eq "MACRO") && ($param_name eq "Returns")) {
4594 # FIXME: do we need to add it then to tmpl_params[] ?
4595 my $num=$#$tmpl_params;
4596 @TRACE@(" adding Returns: to macro docs for $symbol.\n");
4597 $$tmpl_params[$num+1]="Returns";
4598 $$tmpl_params[$num+2]=$$params[$j+1];
4601 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4602 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4608 if ($have_tmpl_docs) {
4609 $AllDocumentedSymbols{$symbol} = 1;
4610 @TRACE@("merging [$symbol] from template\n");
4613 @TRACE@("[$symbol] undocumented\n");
4617 # if this symbol is documented, check if docs are complete
4618 $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4619 # remove all xml-tags and whitespaces
4620 $check_tmpl_doc =~ s/<.*?>//g;
4621 $check_tmpl_doc =~ s/\s//g;
4622 if ($check_tmpl_doc ne "") {
4623 my $tmpl_params = $SymbolParams{$symbol};
4624 if (defined ($tmpl_params)) {
4625 my $type = $DeclarationTypes {$symbol};
4627 my $item = "Parameter";
4628 if (defined ($type)) {
4629 if ($type eq 'STRUCT') {
4631 } elsif ($type eq 'ENUM') {
4633 } elsif ($type eq 'UNION') {
4640 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4642 if ($#$tmpl_params > 0) {
4644 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4645 # Output a warning if the parameter is empty and
4646 # remember for stats.
4647 my $tmpl_param_name = $$tmpl_params[$j];
4648 my $tmpl_param_desc = $$tmpl_params[$j + 1];
4649 if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4650 if (exists ($AllIncompleteSymbols{$symbol})) {
4651 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4653 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4655 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4656 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4661 if ($#$tmpl_params == 0) {
4662 $AllIncompleteSymbols{$symbol}="<items>";
4663 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4664 "$item descriptions for $symbol are missing in source code comment block.");
4666 # $#$tmpl_params==-1 means we don't know about parameters
4667 # this unfortunately does not tell if there should be some
4672 @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4675 #############################################################################
4676 # Function : IsEmptyDoc
4677 # Description : Check if a doc-string is empty. Its also regarded as empty if
4678 # it only consist of whitespace or e.g. FIXME.
4679 # Arguments : the doc-string
4680 #############################################################################
4684 if ($doc =~ /^\s*$/) {
4688 if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4695 #############################################################################
4696 # Function : ConvertMarkDown
4697 # Description : Converts mark down syntax to the respective docbook.
4698 # http://de.wikipedia.org/wiki/Markdown
4699 # Inspired by the design of ParseDown
4700 # http://parsedown.org/
4701 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4702 # Arguments : the symbol name, the doc-string
4703 #############################################################################
4705 sub ConvertMarkDown {
4706 my ($symbol, $text) = @_;
4708 $text = &MarkDownParse ($text, $symbol);
4713 # SUPPORTED MARKDOWN
4714 # ==================
4723 # Setext-style Headers
4724 # --------------------
4732 # Ordered (unnested) Lists
4733 # ------------------------
4737 # 1. item 2 with loooong
4742 # Note: we require a blank line above the list items
4745 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4747 sub MarkDownParseBlocks {
4748 my ($linesref, $symbol, $context) = @_;
4751 my $md_block = { type => "" };
4753 OUTER: foreach $line (@$linesref) {
4754 my $first_char = substr ($line, 0, 1);
4755 my $deindented_line;
4757 @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
4759 if ($md_block->{"type"} eq "markup") {
4760 if (!$md_block->{"closed"}) {
4761 if (index ($line, $md_block->{"start"}) != -1) {
4762 $md_block->{"depth"}++;
4764 if (index ($line, $md_block->{"end"}) != -1) {
4765 if ($md_block->{"depth"} > 0) {
4766 $md_block->{"depth"}--;
4768 @TRACE@("closing tag '$line'");
4769 $md_block->{"closed"} = 1;
4770 # TODO(ensonic): reparse inner text with MarkDownParseLines?
4773 $md_block->{"text"} .= "\n" . $line;
4774 @TRACE@("add to markup");
4779 $deindented_line = $line;
4780 $deindented_line =~ s/^\s+//;
4782 if ($md_block->{"type"} eq "heading") {
4783 # a heading is ended by any level less than or equal
4784 if ($md_block->{"level"} == 1) {
4785 if ($line =~ /^={4,}[ \t]*$/) {
4786 my $text = pop @{$md_block->{"lines"}};
4787 $md_block->{"interrupted"} = 0;
4788 push @md_blocks, $md_block;
4790 $md_block = { type => "heading",
4795 } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4796 $md_block->{"interrupted"} = 0;
4797 push @md_blocks, $md_block;
4799 $md_block = { type => "heading",
4806 # push lines into the block until the end is reached
4807 push @{$md_block->{"lines"}}, $line;
4811 if ($line =~ /^[=]{4,}[ \t]*$/) {
4812 my $text = pop @{$md_block->{"lines"}};
4813 $md_block->{"interrupted"} = 0;
4814 push @md_blocks, $md_block;
4816 $md_block = { type => "heading",
4821 } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4822 my $text = pop @{$md_block->{"lines"}};
4823 $md_block->{"interrupted"} = 0;
4824 push @md_blocks, $md_block;
4826 $md_block = { type => "heading",
4831 } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4832 $md_block->{"interrupted"} = 0;
4833 push @md_blocks, $md_block;
4835 $md_block = { type => "heading",
4839 level => length($1) };
4842 # push lines into the block until the end is reached
4843 push @{$md_block->{"lines"}}, $line;
4847 } elsif ($md_block->{"type"} eq "code") {
4848 if ($line =~ /^[ \t]*\]\|(.*)/) {
4849 push @md_blocks, $md_block;
4850 $md_block = { type => "paragraph",
4854 push @{$md_block->{"lines"}}, $line;
4859 if ($deindented_line eq "") {
4860 $md_block->{"interrupted"} = 1;
4864 if ($md_block->{"type"} eq "quote") {
4865 if (!$md_block->{"interrupted"}) {
4866 $line =~ s/^[ ]*>[ ]?//;
4867 push @{$md_block->{"lines"}}, $line;
4870 } elsif ($md_block->{"type"} eq "li") {
4871 my $marker = $md_block->{"marker"};
4872 if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4873 my $indentation = $1;
4874 if ($md_block->{"indentation"} ne $indentation) {
4875 push @{$md_block->{"lines"}}, $line;
4878 my $ordered = $md_block->{"ordered"};
4879 $lines =~ s/^[ ]{0,4}//;
4880 $md_block->{"last"} = 0;
4881 push @md_blocks, $md_block;
4882 $md_block = { type => "li",
4883 ordered => $ordered,
4884 indentation => $indentation,
4888 lines => [ $lines ] };
4893 if ($md_block->{"interrupted"}) {
4894 if ($first_char eq " ") {
4895 push @{$md_block->{"lines"}}, "";
4896 $line =~ s/^[ ]{0,4}//;
4897 push @{$md_block->{"lines"}}, $line;
4898 $md_block->{"interrupted"} = 0;
4902 $line =~ s/^[ ]{0,4}//;
4903 push @{$md_block->{"lines"}}, $line;
4908 # indentation sensitive types
4909 @TRACE@("parsing '$line'");
4911 if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4913 push @md_blocks, $md_block;
4915 $md_block = { type => "heading",
4919 level => length($1) };
4922 } elsif ($line =~ /^={4,}[ \t]*$/) {
4923 # setext heading (====)
4925 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4926 push @md_blocks, $md_block;
4927 $md_block->{"type"} = "heading";
4928 $md_block->{"lines"} = [];
4929 $md_block->{"level"} = 1;
4933 } elsif ($line =~ /^-{4,}[ \t]*$/) {
4934 # setext heading (-----)
4936 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4937 push @md_blocks, $md_block;
4938 $md_block->{"type"} = "heading";
4939 $md_block->{"lines"} = [];
4940 $md_block->{"level"} = 2;
4944 } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4946 $md_block->{"interrupted"} = 1;
4947 push @md_blocks, $md_block;
4948 $md_block = { type => "code",
4954 # indentation insensitive types
4955 if ($line =~ /^[ ]*<!DOCTYPE/) {
4956 push @md_blocks, $md_block;
4958 $md_block = { type => "markup",
4959 text => $deindented_line,
4965 } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4966 # markup, including <?xml version="1.0"?>
4968 my $is_self_closing = defined($2);
4970 # skip link markdown
4971 # TODO(ensonic): consider adding more uri schemes (ftp, ...)
4972 if ($tag =~ /^https?/) {
4973 @TRACE@("skipping link '$tag'");
4975 # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
4976 # instead of creation a markdown block.
4977 my $scanning_for_end_of_text_level_tag = (
4978 $md_block->{"type"} eq "paragraph" &&
4979 defined($md_block->{"start"}) &&
4980 !$md_block->{"closed"});
4981 @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
4982 if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
4983 push @md_blocks, $md_block;
4985 if ($is_self_closing) {
4986 @TRACE@("self-closing docbook '$tag'");
4987 $md_block = { type => "self-closing tag",
4988 text => $deindented_line };
4989 $is_self_closing = 0;
4993 @TRACE@("new markup '$tag'");
4994 $md_block = { type => "markup",
4995 text => $deindented_line,
4996 start => "<" . $tag . ">",
4997 end => "</" . $tag . ">",
5000 if ($deindented_line =~ /<\/$tag>/) {
5001 $md_block->{"closed"} = 1;
5005 if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
5006 @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
5007 # TODO(ensonic): handle nesting
5008 if (!$scanning_for_end_of_text_level_tag) {
5009 if ($deindented_line !~ /<\/$tag>/) {
5010 @TRACE@("new text level markup '$tag'");
5011 $md_block->{"start"} = "<" . $tag . ">";
5012 $md_block->{"end"} = "</" . $tag . ">";
5013 $md_block->{"closed"} = 0;
5014 @TRACE@("scanning for end of '$tag'");
5017 if ($deindented_line =~ /$md_block->{"end"}/) {
5018 $md_block->{"closed"} = 1;
5019 @TRACE@("found end of '$tag'");
5025 } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
5027 push @md_blocks, $md_block;
5029 my $indentation = $1;
5030 $lines =~ s/^[ ]{0,4}//;
5031 $md_block = { type => "li",
5033 indentation => $indentation,
5037 lines => [ $lines ] };
5039 } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
5040 push @md_blocks, $md_block;
5041 $md_block = { type => "quote",
5047 if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5048 push @md_blocks, $md_block;
5050 my $indentation = $1;
5051 $lines =~ s/^[ ]{0,4}//;
5053 $md_block = { type => "li",
5055 indentation => $indentation,
5056 marker => "\\d+[.]",
5059 lines => [ $lines ] };
5065 if ($md_block->{"type"} eq "paragraph") {
5066 if ($md_block->{"interrupted"}) {
5067 push @md_blocks, $md_block;
5068 $md_block = { type => "paragraph",
5071 @TRACE@("new paragraph due to interrupted");
5073 $md_block->{"text"} .= "\n" . $line;
5074 @TRACE@("add to paragraph");
5077 push @md_blocks, $md_block;
5078 $md_block = { type => "paragraph",
5080 @TRACE@("new paragraph due to different block type");
5084 push @md_blocks, $md_block;
5091 sub MarkDownParseSpanElementsInner {
5092 my ($text, $markersref) = @_;
5094 my %markers = map { $_ => 1 } @$markersref;
5096 while ($text ne "") {
5097 my $closest_marker = "";
5098 my $closest_marker_index = 0;
5099 my $closest_marker_position = -1;
5100 my $text_marker = "";
5107 while ( ($marker, $use) = each %markers ) {
5108 my $marker_position;
5114 $marker_position = index ($text, $marker);
5116 if ($marker_position < 0) {
5117 $markers{$marker} = 0;
5121 if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5122 $closest_marker = $marker;
5123 $closest_marker_index = $i;
5124 $closest_marker_position = $marker_position;
5128 if ($closest_marker_position >= 0) {
5129 $text_marker = substr ($text, $closest_marker_position);
5132 if ($text_marker eq "") {
5138 $markup .= substr ($text, 0, $closest_marker_position);
5139 $text = substr ($text, $closest_marker_position);
5140 @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5142 if ($closest_marker eq "![" || $closest_marker eq "[") {
5145 if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5148 %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5151 $offset = length ($&);
5152 if ($element{"!"}) {
5156 $remaining_text = substr ($text, $offset);
5157 if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5162 $offset += length ($&);
5163 } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5164 $element{"ref"} = $1;
5165 $offset += length ($&);
5172 if ($element{"»"}) {
5173 $element{"»"} =~ s/&/&/g;
5174 $element{"»"} =~ s/</</g;
5176 if ($element{"!"}) {
5177 $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5179 if (defined ($element{"a"})) {
5180 $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5183 $markup .= "</inlinemediaobject>";
5184 } elsif ($element{"ref"}) {
5185 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5186 $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5188 if (defined ($element{"#"})) {
5189 # title attribute not supported
5192 $markup .= ">" . $element{"a"} . "</link>";
5194 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5195 $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5197 if (defined ($element{"#"})) {
5198 # title attribute not supported
5201 $markup .= ">" . $element{"a"} . "</ulink>";
5204 $markup .= $closest_marker;
5205 if ($closest_marker eq "![") {
5211 } elsif ($closest_marker eq "<") {
5212 if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5213 my $element_url = $1;
5214 $element_url =~ s/&/&/g;
5215 $element_url =~ s/</</g;
5217 $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5218 $offset = length ($&);
5219 } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5220 $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5221 $offset = length ($&);
5222 } elsif ($text =~ /^<[^>]+?>/) {
5224 $offset = length ($&);
5229 } elsif ($closest_marker eq "\\") {
5230 my $special_char = substr ($text, 1, 1);
5231 if ($MD_ESCAPABLE_CHARS{$special_char} ||
5232 $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5233 $markup .= $special_char;
5239 } elsif ($closest_marker eq "`") {
5240 if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5241 my $element_text = $2;
5242 $markup .= "<literal>" . $element_text . "</literal>";
5243 $offset = length ($&);
5248 } elsif ($closest_marker eq "@") {
5249 # Convert '@param()'
5250 # FIXME: we could make those also links ($symbol.$2), but that would be less
5251 # useful as the link target is a few lines up or down
5252 if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5253 $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5254 $offset = length ($&);
5255 } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5256 # Convert '@param', but not '\@param'.
5257 $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5258 $offset = length ($&);
5259 } elsif ($text =~ /^\\\@/) {
5261 $offset = length ($&);
5266 } elsif ($closest_marker eq "#") {
5267 if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5268 # handle #Object.func()
5269 $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5270 $offset = length ($&);
5271 } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5272 # Convert '#symbol', but not '\#symbol'.
5273 $markup .= $1 . &MakeHashXRef ($2, "type");
5274 $offset = length ($&);
5275 } elsif ($text =~ /^\\#/) {
5277 $offset = length ($&);
5282 } elsif ($closest_marker eq "%") {
5283 if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5284 # Convert '%constant', but not '\%constant'.
5285 # Also allow negative numbers, e.g. %-1.
5286 $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5287 $offset = length ($&);
5288 } elsif ($text =~ /^\\%/) {
5290 $offset = length ($&);
5298 $text = substr ($text, $offset);
5305 sub MarkDownParseSpanElements {
5307 my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5309 $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5311 # Convert 'function()' or 'macro()'.
5312 # if there is abc_*_def() we don't want to make a link to _def()
5313 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5314 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5319 sub ReplaceEntities {
5320 my ($text, $symbol) = @_;
5322 my @entities = ( [ "<", "<" ],
5331 [ "&", "&" ] ); # Do this last, or the others get messed up.
5334 # Expand entities in <programlisting> even inside CDATA since
5335 # we changed the definition of |[ to add CDATA
5336 for ($i = 0; $i <= $#entities; $i++) {
5337 $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5343 sub MarkDownOutputDocBook {
5344 my ($blocksref, $symbol, $context) = @_;
5347 my @blocks = @$blocksref;
5349 foreach $block (@blocks) {
5353 #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
5355 if ($block->{"type"} eq "paragraph") {
5356 $text = &MarkDownParseSpanElements ($block->{"text"});
5357 if ($context eq "li" && $output eq "") {
5358 if ($block->{"interrupted"}) {
5359 $output .= "\n<para>$text</para>\n";
5361 $output .= "<para>$text</para>";
5367 $output .= "<para>$text</para>\n";
5370 } elsif ($block->{"type"} eq "heading") {
5373 $title = &MarkDownParseSpanElements ($block->{"text"});
5375 if ($block->{"level"} == 1) {
5381 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5382 if (defined ($block->{"id"})) {
5383 $output .= "<$tag id=\"" . $block->{"id"} . "\">";
5385 $output .= "<$tag>";
5388 $output .= "<title>$title</title>$text</$tag>\n";
5389 } elsif ($block->{"type"} eq "li") {
5390 my $tag = "itemizedlist";
5392 if ($block->{"first"}) {
5393 if ($block->{"ordered"}) {
5394 $tag = "orderedlist";
5396 $output .= "<$tag>\n";
5399 if ($block->{"interrupted"}) {
5400 push @{$block->{"lines"}}, "";
5403 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5404 $output .= "<listitem>".$text."</listitem>\n";
5405 if ($block->{"last"}) {
5406 if ($block->{"ordered"}) {
5407 $tag = "orderedlist";
5409 $output .= "</$tag>\n";
5411 } elsif ($block->{"type"} eq "quote") {
5412 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5413 $output .= "<blockquote>\n$text</blockquote>\n";
5414 } elsif ($block->{"type"} eq "code") {
5415 if ($block->{"language"}) {
5416 $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5418 $output .= "<informalexample><programlisting><![CDATA[\n";
5420 foreach (@{$block->{"lines"}}) {
5421 $output .= &ReplaceEntities ($_, $symbol) . "\n";
5423 $output .= "]]></programlisting></informalexample>\n";
5424 } elsif ($block->{"type"} eq "markup") {
5425 $text = &ExpandAbbreviations($symbol, $block->{"text"});
5426 $output .= $text."\n";
5428 $output .= $block->{"text"}."\n";
5430 #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
5436 sub MarkDownParseLines {
5437 my ($linesref, $symbol, $context) = @_;
5439 my @lines = @$linesref;
5442 @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5443 $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5449 my ($text, $symbol) = @_;
5452 # take out some variability in line endings
5453 $text =~ s%\r\n%\n%g;
5457 @lines = split("\n", $text);
5458 $text = MarkDownParseLines(\@lines, $symbol, "");
5463 #############################################################################
5464 # LIBRARY FUNCTIONS - These functions are used in both gtkdoc-mkdb and
5465 # gtkdoc-mktmpl and should eventually be moved to a
5467 #############################################################################
5469 #############################################################################
5470 # Function : ReadDeclarationsFile
5471 # Description : This reads in a file containing the function/macro/enum etc.
5474 # Note that in some cases there are several declarations with
5475 # the same name, e.g. for conditional macros. In this case we
5476 # set a flag in the %DeclarationConditional hash so the
5477 # declaration is not shown in the docs.
5479 # If a macro and a function have the same name, e.g. for
5480 # gtk_object_ref, the function declaration takes precedence.
5482 # Some opaque structs are just declared with 'typedef struct
5483 # _name name;' in which case the declaration may be empty.
5484 # The structure may have been found later in the header, so
5485 # that overrides the empty declaration.
5487 # Arguments : $file - the declarations file to read
5488 # $override - if declarations in this file should override
5489 # any current declaration.
5490 #############################################################################
5492 sub ReadDeclarationsFile {
5493 my ($file, $override) = @_;
5495 if ($override == 0) {
5497 %DeclarationTypes = ();
5498 %DeclarationConditional = ();
5499 %DeclarationOutput = ();
5503 || die "Can't open $file: $!";
5504 my $declaration_type = "";
5505 my $declaration_name;
5507 my $is_deprecated = 0;
5509 if (!$declaration_type) {
5510 if (m/^<([^>]+)>/) {
5511 $declaration_type = $1;
5512 $declaration_name = "";
5513 @TRACE@("Found declaration: $declaration_type\n");
5517 if (m%^<NAME>(.*)</NAME>%) {
5518 $declaration_name = $1;
5519 } elsif (m%^<DEPRECATED/>%) {
5521 } elsif (m%^</$declaration_type>%) {
5522 @TRACE@("Found end of declaration: $declaration_name\n");
5523 # Check that the declaration has a name
5524 if ($declaration_name eq "") {
5525 &LogWarning ($file, $., "$declaration_type has no name.\n");
5528 # If the declaration is an empty typedef struct _XXX XXX
5529 # set the flag to indicate the struct has a typedef.
5530 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5531 && $declaration =~ m/^\s*$/) {
5532 @TRACE@("Struct has typedef: $declaration_name\n");
5533 $StructHasTypedef{$declaration_name} = 1;
5536 # Check if the symbol is already defined.
5537 if (defined ($Declarations{$declaration_name})
5538 && $override == 0) {
5539 # Function declarations take precedence.
5540 if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5542 } elsif ($declaration_type eq 'FUNCTION') {
5543 if ($is_deprecated) {
5544 $Deprecated{$declaration_name} = "";
5546 $Declarations{$declaration_name} = $declaration;
5547 $DeclarationTypes{$declaration_name} = $declaration_type;
5548 } elsif ($DeclarationTypes{$declaration_name}
5549 eq $declaration_type) {
5550 # If the existing declaration is empty, or is just a
5551 # forward declaration of a struct, override it.
5552 if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5553 if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5554 if ($is_deprecated) {
5555 $Deprecated{$declaration_name} = "";
5557 $Declarations{$declaration_name} = $declaration;
5558 } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5559 # Ignore an empty or forward declaration.
5561 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5564 # set flag in %DeclarationConditional hash for
5565 # multiply defined macros/typedefs.
5566 $DeclarationConditional{$declaration_name} = 1;
5569 &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5572 if ($is_deprecated) {
5573 $Deprecated{$declaration_name} = "";
5575 $Declarations{$declaration_name} = $declaration;
5576 $DeclarationTypes{$declaration_name} = $declaration_type;
5579 $declaration_type = "";
5590 #############################################################################
5591 # Function : ReadSignalsFile
5592 # Description : This reads in an existing file which contains information on
5593 # all GTK signals. It creates the arrays @SignalNames and
5594 # @SignalPrototypes containing info on the signals. The first
5595 # line of the SignalPrototype is the return type of the signal
5596 # handler. The remaining lines are the parameters passed to it.
5597 # The last parameter, "gpointer user_data" is always the same
5598 # so is not included.
5599 # Arguments : $file - the file containing the signal handler prototype
5601 #############################################################################
5603 sub ReadSignalsFile {
5611 my $signal_prototype;
5613 # Reset the signal info.
5614 @SignalObjects = ();
5616 @SignalReturns = ();
5618 @SignalPrototypes = ();
5623 if (!open (INPUT, $file)) {
5624 warn "Can't open $file - skipping signals\n";
5631 $signal_object = "";
5633 $signal_returns = "";
5634 $signal_prototype = "";
5637 if (m/^<NAME>(.*)<\/NAME>/) {
5639 if ($signal_name =~ m/^(.*)::(.*)$/) {
5640 $signal_object = $1;
5641 ($signal_name = $2) =~ s/_/-/g;
5642 @TRACE@("Found signal: $signal_name\n");
5644 &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5646 } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5647 $signal_returns = $1;
5648 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5650 } elsif (m%^</SIGNAL>%) {
5651 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5652 push (@SignalObjects, $signal_object);
5653 push (@SignalNames, $signal_name);
5654 push (@SignalReturns, $signal_returns);
5655 push (@SignalFlags, $signal_flags);
5656 push (@SignalPrototypes, $signal_prototype);
5659 $signal_prototype .= $_;
5667 #############################################################################
5668 # Function : ReadTemplateFile
5669 # Description : This reads in the manually-edited documentation file
5670 # corresponding to the file currently being created, so we can
5671 # insert the documentation at the appropriate places.
5672 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5673 # is a hash of arrays.
5674 # NOTE: This function is duplicated in gtkdoc-mktmpl (but
5675 # slightly different).
5676 # Arguments : $docsfile - the template file to read in.
5677 # $skip_unused_params - 1 if the unused parameters should be
5679 #############################################################################
5681 sub ReadTemplateFile {
5682 my ($docsfile, $skip_unused_params) = @_;
5684 my $template = "$docsfile.sgml";
5685 if (! -f $template) {
5686 @TRACE@("File doesn't exist: $template\n");
5690 # start with empty hashes, we merge the source comment for each file
5696 my $current_type = ""; # Type of symbol being read.
5697 my $current_symbol = ""; # Name of symbol being read.
5698 my $symbol_doc = ""; # Description of symbol being read.
5699 my @params; # Parameter names and descriptions of current
5700 # function/macro/function typedef.
5701 my $current_param = -1; # Index of parameter currently being read.
5702 # Note that the param array contains pairs
5703 # of param name & description.
5704 my $in_unused_params = 0; # True if we are reading in the unused params.
5705 my $in_deprecated = 0;
5707 my $in_stability = 0;
5709 open (DOCS, "$template")
5710 || die "Can't open $template: $!";
5712 @TRACE@("reading template $template");
5715 if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5718 if ($symbol eq "Title"
5719 || $symbol eq "Short_Description"
5720 || $symbol eq "Long_Description"
5721 || $symbol eq "See_Also"
5722 || $symbol eq "Stability_Level"
5723 || $symbol eq "Include"
5724 || $symbol eq "Image") {
5726 $symbol = $docsfile . ":" . $symbol;
5729 @TRACE@("Found symbol: $symbol\n");
5730 # Remember file and line for the symbol
5731 $SymbolSourceFile{$symbol} = $template;
5732 $SymbolSourceLine{$symbol} = $.;
5734 # Store previous symbol, but remove any trailing blank lines.
5735 if ($current_symbol ne "") {
5736 $symbol_doc =~ s/\s+$//;
5737 $SymbolTypes{$current_symbol} = $current_type;
5738 $SymbolDocs{$current_symbol} = $symbol_doc;
5740 # Check that the stability level is valid.
5741 if ($StabilityLevel{$current_symbol}) {
5742 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5745 if ($current_param >= 0) {
5746 $SymbolParams{$current_symbol} = [ @params ];
5748 # Delete any existing params in case we are overriding a
5749 # previously read template.
5750 delete $SymbolParams{$current_symbol};
5753 $current_type = $type;
5754 $current_symbol = $symbol;
5755 $current_param = -1;
5756 $in_unused_params = 0;
5763 } elsif (m/^<!-- # Unused Parameters # -->/) {
5764 @TRACE@("Found unused parameters\n");
5765 $in_unused_params = 1;
5768 } elsif ($in_unused_params && $skip_unused_params) {
5769 # When outputting the DocBook we skip unused parameters.
5770 @TRACE@("Skipping unused param: $_");
5774 # Check if param found. Need to handle "..." and "format...".
5775 if (s/^\@([\w\.]+):\040?//) {
5776 my $param_name = $1;
5777 my $param_desc = $_;
5778 # Allow variations of 'Returns'
5779 if ($param_name =~ m/^[Rr]eturns?$/) {
5780 $param_name = "Returns";
5782 # Allow varargs variations
5783 if ($param_name =~ m/^.*\.\.\.$/) {
5784 $param_name = "...";
5787 # strip trailing whitespaces and blank lines
5790 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5792 if ($param_name eq "Deprecated") {
5794 $Deprecated{$current_symbol} = $_;
5795 } elsif ($param_name eq "Since") {
5798 $Since{$current_symbol} = $_;
5799 } elsif ($param_name eq "Stability") {
5801 $StabilityLevel{$current_symbol} = $_;
5803 push (@params, $param_name);
5804 push (@params, $param_desc);
5805 $current_param += $PARAM_FIELD_COUNT;
5808 # strip trailing whitespaces and blank lines
5813 if ($in_deprecated) {
5814 $Deprecated{$current_symbol} .= $_;
5815 } elsif ($in_since) {
5816 &LogWarning ($template, $., "multi-line since docs found");
5817 #$Since{$current_symbol} .= $_;
5818 } elsif ($in_stability) {
5819 $StabilityLevel{$current_symbol} .= $_;
5820 } elsif ($current_param >= 0) {
5821 $params[$current_param] .= $_;
5830 # Remember to finish the current symbol doccs.
5831 if ($current_symbol ne "") {
5833 $symbol_doc =~ s/\s+$//;
5834 $SymbolTypes{$current_symbol} = $current_type;
5835 $SymbolDocs{$current_symbol} = $symbol_doc;
5837 # Check that the stability level is valid.
5838 if ($StabilityLevel{$current_symbol}) {
5839 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5842 if ($current_param >= 0) {
5843 $SymbolParams{$current_symbol} = [ @params ];
5845 # Delete any existing params in case we are overriding a
5846 # previously read template.
5847 delete $SymbolParams{$current_symbol};
5856 #############################################################################
5857 # Function : ReadObjectHierarchy
5858 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5859 # the GtkObject subclasses described in this module (and their
5861 # It places them in the @Objects array, and places their level
5862 # in the object hierarchy in the @ObjectLevels array, at the
5863 # same index. GtkObject, the root object, has a level of 1.
5865 # This also generates tree_index.sgml as it goes along.
5868 #############################################################################
5870 sub ReadObjectHierarchy {
5874 if (! -f $OBJECT_TREE_FILE) {
5877 if (!open (INPUT, $OBJECT_TREE_FILE)) {
5878 warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5882 # Only emit objects if they are supposed to be documented, or if
5883 # they have documented children. To implement this, we maintain a
5884 # stack of pending objects which will be emitted if a documented
5886 my @pending_objects = ();
5887 my @pending_levels = ();
5893 my $level = (length($`)) / 2 + 1;
5900 while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5901 my $pobject = pop(@pending_objects);
5902 my $plevel = pop(@pending_levels);
5905 push (@pending_objects, $object);
5906 push (@pending_levels, $level);
5908 if (exists($KnownSymbols{$object})) {
5909 while ($#pending_levels >= 0) {
5910 $object = shift @pending_objects;
5911 $level = shift @pending_levels;
5912 $xref = &MakeXRef ($object);
5914 push (@tree, ' ' x ($level * 4) . "$xref");
5915 push (@Objects, $object);
5916 push (@ObjectLevels, $level);
5917 $ObjectRoots{$object} = $root;
5921 # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5928 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
5929 my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
5930 my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
5932 open (OUTPUT, ">$new_tree_index")
5933 || die "Can't create $new_tree_index: $!";
5935 print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
5938 &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5943 #############################################################################
5944 # Function : ReadInterfaces
5945 # Description : This reads in the $MODULE.interfaces file.
5948 #############################################################################
5950 sub ReadInterfaces {
5953 if (! -f $INTERFACES_FILE) {
5956 if (!open (INPUT, $INTERFACES_FILE)) {
5957 warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5963 my ($object, @ifaces) = split;
5964 if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5965 my @knownIfaces = ();
5967 # filter out private interfaces, but leave foreign interfaces
5968 foreach my $iface (@ifaces) {
5969 if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5970 push (@knownIfaces, $iface);
5974 $Interfaces{$object} = join(' ', @knownIfaces);
5975 @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
5977 @TRACE@("skipping interfaces for unknown symbol: $object\n");
5983 #############################################################################
5984 # Function : ReadPrerequisites
5985 # Description : This reads in the $MODULE.prerequisites file.
5988 #############################################################################
5990 sub ReadPrerequisites {
5991 %Prerequisites = ();
5993 if (! -f $PREREQUISITES_FILE) {
5996 if (!open (INPUT, $PREREQUISITES_FILE)) {
5997 warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
6003 my ($iface, @prereqs) = split;
6004 if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
6005 my @knownPrereqs = ();
6007 # filter out private prerequisites, but leave foreign prerequisites
6008 foreach my $prereq (@prereqs) {
6009 if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
6010 push (@knownPrereqs, $prereq);
6014 $Prerequisites{$iface} = join(' ', @knownPrereqs);
6020 #############################################################################
6021 # Function : ReadArgsFile
6022 # Description : This reads in an existing file which contains information on
6023 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
6024 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
6026 # Arguments : $file - the file containing the arg information.
6027 #############################################################################
6042 # Reset the args info.
6055 if (!open (INPUT, $file)) {
6056 warn "Can't open $file - skipping args\n";
6073 if (m/^<NAME>(.*)<\/NAME>/) {
6075 if ($arg_name =~ m/^(.*)::(.*)$/) {
6077 ($arg_name = $2) =~ s/_/-/g;
6078 @TRACE@("Found arg: $arg_name\n");
6080 &LogWarning ($file, $., "Invalid argument name: $arg_name");
6082 } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6084 } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6086 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6088 } elsif (m/^<NICK>(.*)<\/NICK>/) {
6090 } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6092 if ($arg_blurb eq "(null)") {
6094 &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6096 } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6098 } elsif (m%^</ARG>%) {
6099 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6100 push (@ArgObjects, $arg_object);
6101 push (@ArgNames, $arg_name);
6102 push (@ArgTypes, $arg_type);
6103 push (@ArgRanges, $arg_range);
6104 push (@ArgFlags, $arg_flags);
6105 push (@ArgNicks, $arg_nick);
6106 push (@ArgBlurbs, $arg_blurb);
6107 push (@ArgDefaults, $arg_default);
6115 #############################################################################
6116 # Function : AddTreeLineArt
6117 # Description : Add unicode lineart to a pre-indented string array and returns
6118 # it as as multiline string.
6119 # Arguments : @tree - array of indented strings.
6120 #############################################################################
6122 sub AddTreeLineArt {
6123 my @tree = @{$_[0]};
6128 # iterate bottom up over the tree
6129 for ($i = $#tree; $i >= 0; $i--) {
6130 # count leading spaces
6131 $tree[$i] =~ /^([^<A-Za-z]*)/;
6132 $indent = length( $1 );
6133 # replace with ╰───, if place of ╰ is not space insert ├
6135 if (substr($tree[$i],$indent-4,1) eq " ") {
6136 substr($tree[$i],$indent-4,4) = "--- ";
6138 substr($tree[$i],$indent-4,4) = "+-- ";
6140 # go lines up while space and insert |
6141 for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6142 substr($tree[$j],$indent-4,1) = '|';
6147 my $res = join("\n", @tree);
6148 # unicode chars for: ╰──
6149 $res =~ s%---%<phrase role=\"lineart\">╰──</phrase>%g;
6150 # unicde chars for: ├──
6151 $res =~ s%\+--%<phrase role=\"lineart\">├──</phrase>%g;
6152 # unicode char for: │
6153 $res =~ s%\|%<phrase role=\"lineart\">│</phrase>%g;
6159 #############################################################################
6160 # Function : CheckIsObject
6161 # Description : Returns 1 if the given name is a GObject or a subclass.
6162 # It uses the global @Objects array.
6163 # Note that the @Objects array only contains classes in the
6164 # current module and their ancestors - not all GObject classes.
6165 # Arguments : $name - the name to check.
6166 #############################################################################
6170 my $root = $ObjectRoots{$name};
6171 # Let GBoxed pass as an object here to get -struct appended to the id
6172 # and prevent conflicts with sections.
6173 return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6177 #############################################################################
6178 # Function : MakeReturnField
6179 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6180 # Arguments : $str - the string to pad.
6181 #############################################################################
6183 sub MakeReturnField {
6186 return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6189 #############################################################################
6190 # Function : GetSymbolSourceFile
6191 # Description : Get the filename where the symbol docs where taken from.
6192 # Arguments : $symbol - the symbol name
6193 #############################################################################
6195 sub GetSymbolSourceFile {
6198 if (defined($SourceSymbolSourceFile{$symbol})) {
6199 return $SourceSymbolSourceFile{$symbol};
6200 } elsif (defined($SymbolSourceFile{$symbol})) {
6201 return $SymbolSourceFile{$symbol};
6207 #############################################################################
6208 # Function : GetSymbolSourceLine
6209 # Description : Get the file line where the symbol docs where taken from.
6210 # Arguments : $symbol - the symbol name
6211 #############################################################################
6213 sub GetSymbolSourceLine {
6216 if (defined($SourceSymbolSourceLine{$symbol})) {
6217 return $SourceSymbolSourceLine{$symbol};
6218 } elsif (defined($SymbolSourceLine{$symbol})) {
6219 return $SymbolSourceLine{$symbol};