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 'not nullable' => "NULL must not be passed as the value in, out, in-out; or as a return value.",
161 'optional' => "NULL may be passed instead of a pointer to a location.",
162 'array' => "Parameter points to an array of items.",
163 'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
164 'attributes' => "Free-form key-value pairs.",
165 'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
166 'constructor' => "This symbol is a constructor, not a static method.",
167 'destroy' => "This parameter is a 'destroy_data', for callbacks.",
168 'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
169 'element-type' => "Generics and defining elements of containers and arrays.",
170 'error-domains' => "Typed errors. Similar to throws in Java.",
171 'foreign' => "This is a foreign struct.",
172 'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
173 'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
174 'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
175 'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
176 'method' => "This is a method",
177 'not-error' => "A GError parameter is not to be handled like a normal GError.",
178 'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
179 'out caller-allocates' => "Out parameter, where caller must allocate storage.",
180 'out callee-allocates' => "Out parameter, where caller must allocate storage.",
181 'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
182 'rename-to' => "Rename the original symbol's name to SYMBOL.",
183 'scope call' => "The callback is valid only during the call to the method.",
184 'scope async' => "The callback is valid until first called.",
185 'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
186 'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
187 'skip' => "Exposed in C code, not necessarily available in other languages.",
188 'transfer container' => "Free data container after the code is done.",
189 'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
190 'transfer full' => "Free data after the code is done.",
191 'transfer none' => "Don't free data after the code is done.",
192 'type' => "Override the parsed C type with given type.",
193 'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
194 'virtual' => "This is the invoker for a virtual method.",
195 'value' => "The specified value overrides the evaluated value of the constant.",
196 # Stability Level definition
197 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
199 The intention of a Stable interface is to enable arbitrary third parties to
200 develop applications to these interfaces, release them, and have confidence that
201 they will run on all minor releases of the product (after the one in which the
202 interface was introduced, and within the same major release). Even at a major
203 release, incompatible changes are expected to be rare, and to have strong
207 Unstable interfaces are experimental or transitional. They are typically used to
208 give outside developers early access to new or rapidly changing technology, or
209 to provide an interim solution to a problem where a more general solution is
210 anticipated. No claims are made about either source or binary compatibility from
211 one minor release to the next.
213 The Unstable interface level is a warning that these interfaces are subject to
214 change without warning and should not be used in unbundled products.
216 Given such caveats, customer impact need not be a factor when considering
217 incompatible changes to an Unstable interface in a major or minor release.
218 Nonetheless, when such changes are introduced, the changes should still be
219 mentioned in the release notes for the affected release.
222 An interface that can be used within the GNOME stack itself, but that is not
223 documented for end-users. Such functions should only be used in specified and
228 # Elements to consider non-block items in MarkDown parsing
229 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
243 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
259 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
262 # Function and other declaration output settings.
263 my $RETURN_TYPE_FIELD_WIDTH = 20;
264 my $SYMBOL_FIELD_WIDTH = 36;
265 my $MAX_SYMBOL_FIELD_WIDTH = 40;
266 my $SIGNAL_FIELD_WIDTH = 16;
267 my $PARAM_FIELD_COUNT = 2;
269 # XML, SGML formatting helper
273 run() unless caller; # Run program unless loaded as a module
277 my %optctl = ('module' => \$MODULE,
278 'source-dir' => \@SOURCE_DIRS,
279 'source-suffixes' => \$SOURCE_SUFFIXES,
280 'ignore-files' => \$IGNORE_FILES,
281 'output-dir' => \$DB_OUTPUT_DIR,
282 'tmpl-dir' => \$TMPL_DIR,
283 'version' => \$PRINT_VERSION,
284 'help' => \$PRINT_HELP,
285 'main-sgml-file' => \$MAIN_SGML_FILE,
286 'expand-content-files' => \$EXPAND_CONTENT_FILES,
287 'sgml-mode' => \$INLINE_MARKUP_MODE,
288 'xml-mode' => \$INLINE_MARKUP_MODE,
289 'default-stability' => \$DEFAULT_STABILITY,
290 'default-includes' => \$DEFAULT_INCLUDES,
291 'output-format' => \$OUTPUT_FORMAT,
292 'name-space' => \$NAME_SPACE,
293 'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
294 'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
296 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
297 "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version",
298 "outputallsymbols", "outputsymbolswithoutsince",
299 "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
300 "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
301 "output-format:s", "name-space:s");
303 if ($PRINT_VERSION) {
312 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
313 && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
319 gtkdoc-mkdb version @VERSION@ - generate docbook files
321 --module=MODULE_NAME Name of the doc module being parsed
322 --source-dir=DIRNAME Directories which contain inline reference material
323 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
324 --ignore-files=FILES A space-separated list of header files/dirs not to
326 --output-dir=DIRNAME Directory to put the generated DocBook files in
327 --tmpl-dir=DIRNAME Directory in which template files may be found
328 --main-sgml-file=FILE File containing the toplevel DocBook file.
329 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
330 --output-format=FORMAT Format to use for the generated docbook, XML or SGML.
331 --{xml,sgml}-mode Allow DocBook markup in inline documentation.
332 --default-stability=LEVEL Specify default stability Level. Valid values are
333 Stable, Unstable, or Private.
334 --default-includes=FILENAMES Specify default includes for section Synopsis
335 --name-space=NS Omit namespace in index.
336 --version Print the version of this program
337 --help Print this help
342 @TRACE@(" ignore files: [$IGNORE_FILES]\n");
344 # check output format
345 if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
346 $OUTPUT_FORMAT = "xml";
348 $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
350 if ($OUTPUT_FORMAT ne "xml") {
351 die "Invalid format '$OUTPUT_FORMAT' passed to --output.format"
354 if (!$MAIN_SGML_FILE) {
355 # backwards compatibility
356 if (-e "${MODULE}-docs.sgml") {
357 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
359 $MAIN_SGML_FILE = "${MODULE}-docs.xml";
363 # extract docbook header or define default
364 if (-e $MAIN_SGML_FILE) {
365 open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
366 $doctype_header = "";
368 if (/^\s*<(book|chapter|article)/) {
369 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
370 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
371 $doctype_header = "";
375 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
376 # FIXME: not sure if we can do this now, as people already work-around the problem
377 # s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#;
378 s#<!ENTITY % gtkdocentities SYSTEM \"([^"]*)\">#<!ENTITY % gtkdocentities SYSTEM \"../$1\">#;
379 $doctype_header .= $_;
383 $doctype_header = <<EOF;
384 <?xml version="1.0"?>
385 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
386 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
388 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
389 <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
394 chomp($doctype_header);
396 # All the files are written in subdirectories beneath here.
397 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
399 # This is where we put all the DocBook output.
400 $DB_OUTPUT_DIR = $DB_OUTPUT_DIR ? $DB_OUTPUT_DIR : "$ROOT_DIR/xml";
402 # This file contains the object hierarchy.
403 $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
405 # This file contains the interfaces.
406 $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
408 # This file contains the prerequisites.
409 $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
411 # This file contains signal arguments and names.
412 $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
414 # The file containing Arg information.
415 $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
417 # Create the root DocBook output directory if it doens't exist.
418 if (! -e $DB_OUTPUT_DIR) {
419 mkdir ("$DB_OUTPUT_DIR", 0777)
420 || die "Can't create directory: $DB_OUTPUT_DIR";
423 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
424 &ReadSignalsFile ($SIGNALS_FILE);
425 &ReadArgsFile ($ARGS_FILE);
426 &ReadObjectHierarchy;
430 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
431 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
432 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
435 for my $dir (@SOURCE_DIRS) {
436 &ReadSourceDocumentation ($dir);
439 my $changed = &OutputDB ("$ROOT_DIR/$MODULE-sections.txt");
441 # If any of the DocBook files have changed, update the timestamp file (so
442 # it can be used for Makefile dependencies).
443 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
445 # try to detect the common prefix
446 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
447 if ($NAME_SPACE eq "") {
454 foreach my $symbol (keys(%IndexEntriesFull)) {
455 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
456 if (length($symbol)>$pos) {
457 $letter=substr($symbol,$pos,1);
458 # stop prefix scanning
459 if ($letter eq "_") {
463 # Should we also stop on a uppercase char, if last was lowercase
464 # GtkWidget, if we have the 'W' and had the 't' before
465 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
466 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
467 # need to recound each time as this is per symbol
468 $prefix{uc($letter)}++;
472 if ($letter ne "" && $letter ne "_") {
475 foreach $letter (keys(%prefix)) {
476 #print "$letter: $prefix{$letter}.\n";
477 if ($prefix{$letter}>$maxsymbols) {
479 $maxsymbols=$prefix{$letter};
482 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
483 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
486 $NAME_SPACE .= $maxletter;
493 } while ($ratio > 0.9);
494 #print "most symbols start with $NAME_SPACE\n";
498 &OutputDeprecatedIndex;
500 &OutputAnnotationGlossary;
502 open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
503 || die "Can't create $ROOT_DIR/sgml.stamp: $!";
504 print (TIMESTAMP "timestamp");
509 #############################################################################
510 # Function : OutputObjectList
511 # Description : This outputs the alphabetical list of objects, in a columned
513 # FIXME: Currently this also outputs ancestor objects
514 # which may not actually be in this module.
516 #############################################################################
518 sub OutputObjectList {
522 # my $old_object_index = "$DB_OUTPUT_DIR/object_index.xml";
523 my $old_object_index = "$DB_OUTPUT_DIR/object_index.sgml";
524 my $new_object_index = "$DB_OUTPUT_DIR/object_index.new";
526 open (OUTPUT, ">$new_object_index")
527 || die "Can't create $new_object_index: $!";
529 print (OUTPUT <<EOF);
530 ${\( MakeDocHeader ("informaltable") )}
531 <informaltable pgwide="1" frame="none">
532 <tgroup cols="$cols">
533 <colspec colwidth="1*"/>
534 <colspec colwidth="1*"/>
535 <colspec colwidth="1*"/>
541 foreach $object (sort (@Objects)) {
542 my $xref = &MakeXRef ($object);
543 if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
544 print (OUTPUT "<entry>$xref</entry>\n");
545 if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
549 # emit an empty row, since empty tables are invalid
550 print (OUTPUT "<row><entry> </entry></row>\n");
553 if ($count % $cols > 0) {
554 print (OUTPUT "</row>\n");
558 print (OUTPUT <<EOF);
559 </tbody></tgroup></informaltable>
563 &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
566 #############################################################################
567 # Function : TrimTextBlock
568 # Description : Trims extra whitespace. Empty lines inside a block are
570 # Arguments : $desc - the text block to trim. May contain newlines.
571 #############################################################################
576 # strip leading spaces on the block
578 # strip trailing spaces on every line
579 $desc =~ s/\s+$/\n/mg;
585 #############################################################################
586 # Function : OutputDB
587 # Description : This collects the output for each section of the docs, and
588 # outputs each file when the end of the section is found.
589 # Arguments : $file - the $MODULE-sections.txt file which contains all of
590 # the functions/macros/structs etc. being documented, organised
591 # into sections and subsections.
592 #############################################################################
597 @TRACE@("Reading: $file\n");
599 || die "Can't open $file: $!";
602 my $book_bottom = "";
603 my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
604 my $section_includes = "";
611 my $functions_synop = "";
612 my $other_synop = "";
613 my $functions_details = "";
614 my $other_details = "";
615 my $signals_synop = "";
616 my $signals_desc = "";
618 my $child_args_synop = "";
619 my $style_args_synop = "";
621 my $child_args_desc = "";
622 my $style_args_desc = "";
623 my $hierarchy_str = "";
626 my $implementations = "";
627 my $prerequisites = "";
629 my @file_objects = ();
631 my %symbol_def_line = ();
633 # merge the source docs, in case there are no templates
634 &MergeSourceDocumentation;
640 } elsif (m/^<SECTION>/) {
644 %symbol_def_line = ();
646 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
647 $other_synop .= "\n";
648 $functions_synop .= "\n";
651 } elsif (m/^<SUBSECTION>/) {
653 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
655 @TRACE@("Section: $title\n");
657 # We don't want warnings if object & class structs aren't used.
658 $DeclarationOutput{$title} = 1;
659 $DeclarationOutput{"${title}Class"} = 1;
660 $DeclarationOutput{"${title}Iface"} = 1;
661 $DeclarationOutput{"${title}Interface"} = 1;
663 } elsif (m/^<FILE>(.*)<\/FILE>/) {
665 if (! defined $templates{$filename}) {
666 if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
667 &MergeSourceDocumentation;
668 $templates{$filename}=$.;
671 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
672 "Previous occurrence on line ".$templates{$filename}.".");
674 if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
675 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
676 # Remove trailing blanks
680 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
682 $section_includes = $1;
684 if (defined $DEFAULT_INCLUDES) {
685 &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
692 } elsif (m/^<\/SECTION>/) {
693 @TRACE@("End of section: $title\n");
694 if ($num_symbols > 0) {
696 $book_bottom .= " <xi:include href=\"xml/$filename.xml\"/>\n";
698 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
699 if ($section_includes) {
700 &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
702 $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
704 if ($section_includes eq "") {
705 $section_includes = $includes;
708 $signals_synop =~ s/^\n*//g;
709 $signals_synop =~ s/\n+$/\n/g;
710 if ($signals_synop ne '') {
711 $signals_synop = <<EOF;
712 <refsect1 id="$section_id.signals" role="signal_proto">
713 <title role="signal_proto.title">Signals</title>
714 <informaltable frame="none">
716 <colspec colname="signals_return" colwidth="150px"/>
717 <colspec colname="signals_name" colwidth="300px"/>
718 <colspec colname="signals_flags" colwidth="200px"/>
726 $signals_desc = TrimTextBlock($signals_desc);
727 $signals_desc = <<EOF;
728 <refsect1 id="$section_id.signal-details" role="signals">
729 <title role="signals.title">Signal Details</title>
735 $args_synop =~ s/^\n*//g;
736 $args_synop =~ s/\n+$/\n/g;
737 if ($args_synop ne '') {
739 <refsect1 id="$section_id.properties" role="properties">
740 <title role="properties.title">Properties</title>
741 <informaltable frame="none">
743 <colspec colname="properties_type" colwidth="150px"/>
744 <colspec colname="properties_name" colwidth="300px"/>
745 <colspec colname="properties_flags" colwidth="200px"/>
753 $args_desc = TrimTextBlock($args_desc);
755 <refsect1 id="$section_id.property-details" role="property_details">
756 <title role="property_details.title">Property Details</title>
762 $child_args_synop =~ s/^\n*//g;
763 $child_args_synop =~ s/\n+$/\n/g;
764 if ($child_args_synop ne '') {
765 $args_synop .= <<EOF;
766 <refsect1 id="$section_id.child-properties" role="child_properties">
767 <title role="child_properties.title">Child Properties</title>
768 <informaltable frame="none">
770 <colspec colname="child_properties_type" colwidth="150px"/>
771 <colspec colname="child_properties_name" colwidth="300px"/>
772 <colspec colname="child_properties_flags" colwidth="200px"/>
780 $child_args_desc = TrimTextBlock($child_args_desc);
782 <refsect1 id="$section_id.child-property-details" role="child_property_details">
783 <title role="child_property_details.title">Child Property Details</title>
789 $style_args_synop =~ s/^\n*//g;
790 $style_args_synop =~ s/\n+$/\n/g;
791 if ($style_args_synop ne '') {
792 $args_synop .= <<EOF;
793 <refsect1 id="$section_id.style-properties" role="style_properties">
794 <title role="style_properties.title">Style Properties</title>
795 <informaltable frame="none">
797 <colspec colname="style_properties_type" colwidth="150px"/>
798 <colspec colname="style_properties_name" colwidth="300px"/>
799 <colspec colname="style_properties_flags" colwidth="200px"/>
807 $style_args_desc = TrimTextBlock($style_args_desc);
809 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
810 <title role="style_properties_details.title">Style Property Details</title>
816 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
817 if ($hierarchy_str ne "") {
818 $hierarchy_str = <<EOF;
819 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
820 <title role="object_hierarchy.title">Object Hierarchy</title>
821 <screen>$hierarchy_str
827 $interfaces =~ TrimTextBlock($interfaces);
828 if ($interfaces ne "") {
830 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
831 <title role="impl_interfaces.title">Implemented Interfaces</title>
837 $implementations = TrimTextBlock($implementations);
838 if ($implementations ne "") {
839 $implementations = <<EOF;
840 <refsect1 id="$section_id.implementations" role="implementations">
841 <title role="implementations.title">Known Implementations</title>
847 $prerequisites = TrimTextBlock($prerequisites);
848 if ($prerequisites ne "") {
849 $prerequisites = <<EOF;
850 <refsect1 id="$section_id.prerequisites" role="prerequisites">
851 <title role="prerequisites.title">Prerequisites</title>
857 $derived = TrimTextBlock($derived);
858 if ($derived ne "") {
860 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
861 <title role="derived_interfaces.title">Known Derived Interfaces</title>
867 $functions_synop =~ s/^\n*//g;
868 $functions_synop =~ s/\n+$/\n/g;
869 if ($functions_synop ne '') {
870 $functions_synop = <<EOF;
871 <refsect1 id="$section_id.functions" role="functions_proto">
872 <title role="functions_proto.title">Functions</title>
873 <informaltable pgwide="1" frame="none">
875 <colspec colname="functions_return" colwidth="150px"/>
876 <colspec colname="functions_name"/>
886 $other_synop =~ s/^\n*//g;
887 $other_synop =~ s/\n+$/\n/g;
888 if ($other_synop ne '') {
889 $other_synop = <<EOF;
890 <refsect1 id="$section_id.other" role="other_proto">
891 <title role="other_proto.title">Types and Values</title>
892 <informaltable role="enum_members_table" pgwide="1" frame="none">
894 <colspec colname="name" colwidth="150px"/>
895 <colspec colname="description"/>
905 my $file_changed = &OutputDBFile ($filename, $title, $section_id,
907 \$functions_synop, \$other_synop,
908 \$functions_details, \$other_details,
909 \$signals_synop, \$signals_desc,
910 \$args_synop, \$args_desc,
911 \$hierarchy_str, \$interfaces,
913 \$prerequisites, \$derived,
923 $section_includes = "";
924 $functions_synop = "";
926 $functions_details = "";
931 $child_args_synop = "";
932 $style_args_synop = "";
934 $child_args_desc = "";
935 $style_args_desc = "";
939 $implementations = "";
943 } elsif (m/^(\S+)/) {
945 @TRACE@(" Symbol: $symbol in subsection: $subsection\n");
947 # check for duplicate entries
948 if (! defined $symbol_def_line{$symbol}) {
949 my $declaration = $Declarations{$symbol};
950 if (defined ($declaration)) {
951 if (&CheckIsObject ($symbol)) {
952 push @file_objects, $symbol;
954 # We don't want standard macros/functions of GObjects,
955 # or private declarations.
956 if ($subsection ne "Standard" && $subsection ne "Private") {
957 my ($synop, $desc) = &OutputDeclaration ($symbol,
959 my $type = $DeclarationTypes {$symbol};
961 if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
962 $functions_synop .= $synop;
963 $functions_details .= $desc;
964 } elsif ($type eq 'MACRO' && $declaration =~ /$symbol\(/) {
965 $functions_synop .= $synop;
966 $functions_details .= $desc;
968 $other_synop .= $synop;
969 $other_details .= $desc;
972 my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
973 my ($arg_synop, $child_arg_synop, $style_arg_synop,
974 $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
975 my $ifaces = &GetInterfaces ($symbol);
976 my $impls = &GetImplementations ($symbol);
977 my $prereqs = &GetPrerequisites ($symbol);
978 my $der = &GetDerived ($symbol);
979 @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
981 $signals_synop .= $sig_synop;
982 $signals_desc .= $sig_desc;
983 $args_synop .= $arg_synop;
984 $child_args_synop .= $child_arg_synop;
985 $style_args_synop .= $style_arg_synop;
986 $args_desc .= $arg_desc;
987 $child_args_desc .= $child_arg_desc;
988 $style_args_desc .= $style_arg_desc;
989 $interfaces .= $ifaces;
990 $implementations .= $impls;
991 $prerequisites .= $prereqs;
994 # Note that the declaration has been output.
995 $DeclarationOutput{$symbol} = 1;
996 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
997 $UndeclaredSymbols{$symbol} = 1;
998 &LogWarning ($file, $., "No declaration found for $symbol.");
1001 $symbol_def_line{$symbol}=$.;
1003 if ($section_id eq "") {
1004 if($title eq "" && $filename eq "") {
1005 &LogWarning ($file, $., "Section has no title and no file.");
1007 # FIXME: one of those would be enough
1008 # filename should be an internal detail for gtk-doc
1011 } elsif ($filename eq "") {
1014 $filename =~ s/\s/_/g;
1016 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1017 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1018 # Remove trailing blanks and use as is
1019 $section_id =~ s/\s+$//;
1020 } elsif (&CheckIsObject ($title)) {
1021 # GObjects use their class name as the ID.
1022 $section_id = &CreateValidSGMLID ($title);
1024 $section_id = &CreateValidSGMLID ("$MODULE-$title");
1027 $SymbolSection{$symbol}=$title;
1028 $SymbolSectionId{$symbol}=$section_id;
1031 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1032 "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1038 &OutputMissingDocumentation;
1039 &OutputUndeclaredSymbols;
1040 &OutputUnusedSymbols;
1042 if ($OUTPUT_ALL_SYMBOLS) {
1045 if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1046 &OutputSymbolsWithoutSince;
1049 for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1050 my $file_changed = &OutputExtraFile ($filename);
1051 if ($file_changed) {
1056 &OutputBook ($book_top, $book_bottom);
1061 #############################################################################
1062 # Function : OutputIndex
1063 # Description : This writes an indexlist that can be included into the main-
1064 # document into an <index> tag.
1065 #############################################################################
1068 my ($basename, $apiindexref ) = @_;
1069 my %apiindex = %{$apiindexref};
1070 my $old_index = "$DB_OUTPUT_DIR/$basename.xml";
1071 my $new_index = "$DB_OUTPUT_DIR/$basename.new";
1072 my $lastletter = " ";
1077 open (OUTPUT, ">$new_index")
1078 || die "Can't create $new_index";
1080 print (OUTPUT &MakeDocHeader ("indexdiv")."\n<indexdiv id=\"$basename\">\n");
1082 @TRACE@("generate $basename index (".%apiindex." entries)\n");
1084 # do a case insensitive sort while chopping off the prefix
1086 sort { $$a{criteria} cmp $$b{criteria} or $$a{original} cmp $$b{original} }
1087 map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1090 $symbol = $$hash{original};
1091 if (defined($$hash{short})) {
1092 $short_symbol = $$hash{short};
1094 $short_symbol = $symbol;
1097 # generate a short symbol description
1098 my $symbol_desc = "";
1099 my $symbol_section = "";
1100 my $symbol_section_id = "";
1101 my $symbol_type = "";
1102 if (defined($DeclarationTypes{$symbol})) {
1103 $symbol_type = lc($DeclarationTypes{$symbol});
1105 if ($symbol_type eq "") {
1106 @TRACE@("trying symbol $symbol\n");
1107 if ($symbol =~ m/(.*)::(.*)/) {
1111 @TRACE@(" trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1112 for ($i = 0; $i <= $#SignalNames; $i++) {
1113 if ($SignalNames[$i] eq $osym) {
1114 $symbol_type = "object signal";
1115 if (defined($SymbolSection{$oname})) {
1116 $symbol_section = $SymbolSection{$oname};
1117 $symbol_section_id = $SymbolSectionId{$oname};
1122 } elsif ($symbol =~ m/(.*):(.*)/) {
1126 @TRACE@(" trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1127 for ($i = 0; $i <= $#ArgNames; $i++) {
1128 @TRACE@(" ".$ArgNames[$i]."\n");
1129 if ($ArgNames[$i] eq $osym) {
1130 $symbol_type = "object property";
1131 if (defined($SymbolSection{$oname})) {
1132 $symbol_section = $SymbolSection{$oname};
1133 $symbol_section_id = $SymbolSectionId{$oname};
1140 if (defined($SymbolSection{$symbol})) {
1141 $symbol_section = $SymbolSection{$symbol};
1142 $symbol_section_id = $SymbolSectionId{$symbol};
1145 if ($symbol_type ne "") {
1146 $symbol_desc=", $symbol_type";
1147 if ($symbol_section ne "") {
1148 $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1149 #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1153 my $curletter = uc(substr($short_symbol,0,1));
1154 my $id = $apiindex{$symbol};
1156 @TRACE@(" add symbol $symbol with $id to index in section $curletter\n");
1158 if ($curletter ne $lastletter) {
1159 $lastletter = $curletter;
1161 if ($divopen == 1) {
1162 print (OUTPUT "</indexdiv>\n");
1164 print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1168 print (OUTPUT <<EOF);
1169 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1173 if ($divopen == 1) {
1174 print (OUTPUT "</indexdiv>\n");
1176 print (OUTPUT "</indexdiv>\n");
1179 &UpdateFileIfChanged ($old_index, $new_index, 0);
1183 #############################################################################
1184 # Function : OutputIndexFull
1185 # Description : This writes the full api indexlist that can be included into the
1186 # main document into an <index> tag.
1187 #############################################################################
1189 sub OutputIndexFull {
1190 &OutputIndex ("api-index-full", \%IndexEntriesFull);
1194 #############################################################################
1195 # Function : OutputDeprecatedIndex
1196 # Description : This writes the deprecated api indexlist that can be included
1197 # into the main document into an <index> tag.
1198 #############################################################################
1200 sub OutputDeprecatedIndex {
1201 &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1205 #############################################################################
1206 # Function : OutputSinceIndexes
1207 # Description : This writes the 'since' api indexlists that can be included into
1208 # the main document into an <index> tag.
1209 #############################################################################
1211 sub OutputSinceIndexes {
1212 my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1214 foreach my $version (@sinces) {
1215 @TRACE@("Since : [$version]\n");
1216 # TODO make filtered hash
1217 #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1218 my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1220 &OutputIndex ("api-index-$version", \%index);
1224 #############################################################################
1225 # Function : OutputAnnotationGlossary
1226 # Description : This writes a glossary of the used annotation terms into a
1227 # separate glossary file that can be included into the main
1229 #############################################################################
1231 sub OutputAnnotationGlossary {
1232 my $old_glossary = "$DB_OUTPUT_DIR/annotation-glossary.xml";
1233 my $new_glossary = "$DB_OUTPUT_DIR/annotation-glossary.new";
1234 my $lastletter = " ";
1237 # if there are no annotations used return
1238 return if (! keys(%AnnotationsUsed));
1240 # add acronyms that are referenced from acronym text
1242 foreach my $annotation (keys(%AnnotationsUsed)) {
1243 if(defined($AnnotationDefinition{$annotation})) {
1244 if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1245 if (!exists($AnnotationsUsed{$1})) {
1246 $AnnotationsUsed{$1} = 1;
1253 open (OUTPUT, ">$new_glossary")
1254 || die "Can't create $new_glossary";
1256 print (OUTPUT <<EOF);
1257 ${\( MakeDocHeader ("glossary") )}
1258 <glossary id="annotation-glossary">
1259 <title>Annotation Glossary</title>
1262 foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1263 if(defined($AnnotationDefinition{$annotation})) {
1264 my $def = $AnnotationDefinition{$annotation};
1265 my $curletter = uc(substr($annotation,0,1));
1267 if ($curletter ne $lastletter) {
1268 $lastletter = $curletter;
1270 if ($divopen == 1) {
1271 print (OUTPUT "</glossdiv>\n");
1273 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1276 print (OUTPUT <<EOF);
1278 <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1287 if ($divopen == 1) {
1288 print (OUTPUT "</glossdiv>\n");
1290 print (OUTPUT "</glossary>\n");
1293 &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1296 #############################################################################
1297 # Function : ReadKnownSymbols
1298 # Description : This collects the names of non-private symbols from the
1299 # $MODULE-sections.txt file.
1300 # Arguments : $file - the $MODULE-sections.txt file which contains all of
1301 # the functions/macros/structs etc. being documented, organised
1302 # into sections and subsections.
1303 #############################################################################
1305 sub ReadKnownSymbols {
1308 my $subsection = "";
1310 @TRACE@("Reading: $file\n");
1312 || die "Can't open $file: $!";
1318 } elsif (m/^<SECTION>/) {
1321 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1324 } elsif (m/^<SUBSECTION>/) {
1327 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1330 } elsif (m/^<FILE>(.*)<\/FILE>/) {
1331 $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1332 $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1335 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1338 } elsif (m/^<\/SECTION>/) {
1341 } elsif (m/^(\S+)/) {
1344 if ($subsection ne "Standard" && $subsection ne "Private") {
1345 $KnownSymbols{$symbol} = 1;
1348 $KnownSymbols{$symbol} = 0;
1356 #############################################################################
1357 # Function : OutputDeclaration
1358 # Description : Returns the synopsis and detailed description DocBook
1359 # describing one function/macro etc.
1360 # Arguments : $symbol - the name of the function/macro begin described.
1361 # $declaration - the declaration of the function/macro.
1362 #############################################################################
1364 sub OutputDeclaration {
1365 my ($symbol, $declaration) = @_;
1367 my $type = $DeclarationTypes {$symbol};
1368 if ($type eq 'MACRO') {
1369 return &OutputMacro ($symbol, $declaration);
1370 } elsif ($type eq 'TYPEDEF') {
1371 return &OutputTypedef ($symbol, $declaration);
1372 } elsif ($type eq 'STRUCT') {
1373 return &OutputStruct ($symbol, $declaration);
1374 } elsif ($type eq 'ENUM') {
1375 return &OutputEnum ($symbol, $declaration);
1376 } elsif ($type eq 'UNION') {
1377 return &OutputUnion ($symbol, $declaration);
1378 } elsif ($type eq 'VARIABLE') {
1379 return &OutputVariable ($symbol, $declaration);
1380 } elsif ($type eq 'FUNCTION') {
1381 return &OutputFunction ($symbol, $declaration, $type);
1382 } elsif ($type eq 'USER_FUNCTION') {
1383 return &OutputFunction ($symbol, $declaration, $type);
1385 die "Unknown symbol type";
1390 #############################################################################
1391 # Function : OutputSymbolTraits
1392 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1393 # Arguments : $symbol - the name of the function/macro begin described.
1394 #############################################################################
1396 sub OutputSymbolTraits {
1400 if (exists $Since{$symbol}) {
1401 my $link_id = "api-index-".$Since{$symbol};
1402 $desc .= "<para role=\"since\">Since: <link linkend=\"$link_id\">$Since{$symbol}</link></para>";
1404 if (exists $StabilityLevel{$symbol}) {
1405 my $stability = $StabilityLevel{$symbol};
1406 $AnnotationsUsed{$stability} = 1;
1407 $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1412 #############################################################################
1413 # Function : Output{Symbol,Section}ExtraLinks
1414 # Description : Returns extralinks for the symbol (if enabled).
1415 # Arguments : $symbol - the name of the function/macro begin described.
1416 #############################################################################
1420 return undef unless defined $text;
1422 # Build a char to hex map
1425 $escapes{chr($_)} = sprintf("%%%02X", $_);
1428 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1429 $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1434 sub OutputSymbolExtraLinks {
1438 if (0) { # NEW FEATURE: needs configurability
1439 my $sstr = &uri_escape($symbol);
1440 my $mstr = &uri_escape($MODULE);
1442 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1443 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$sstr">edit documentation</ulink>
1449 sub OutputSectionExtraLinks {
1450 my ($symbol,$docsymbol) = @_;
1453 if (0) { # NEW FEATURE: needs configurability
1454 my $sstr = &uri_escape($symbol);
1455 my $mstr = &uri_escape($MODULE);
1456 my $dsstr = &uri_escape($docsymbol);
1458 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1459 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$dsstr">edit documentation</ulink>
1466 #############################################################################
1467 # Function : OutputMacro
1468 # Description : Returns the synopsis and detailed description of a macro.
1469 # Arguments : $symbol - the macro.
1470 # $declaration - the declaration of the macro.
1471 #############################################################################
1474 my ($symbol, $declaration) = @_;
1475 my $id = &CreateValidSGMLID ($symbol);
1476 my $condition = &MakeConditionDescription ($symbol);
1477 my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1480 my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1481 my $title = $symbol . (@fields ? "()" : "");
1483 $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1484 $desc .= MakeIndexterms($symbol, $id);
1486 $desc .= OutputSymbolExtraLinks($symbol);
1489 $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1491 $synop .= "</entry></row>\n";
1493 # Don't output the macro definition if is is a conditional macro or it
1494 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1495 # longer than 2 lines, otherwise we get lots of complicated macros like
1497 if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1498 && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1499 my $decl_out = &CreateValidSGML ($declaration);
1500 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1502 $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1503 if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1505 my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1506 # Align each line so that if should all line up OK.
1507 $args =~ s/\n/\n$pad/gm;
1508 $desc .= &CreateValidSGML ($args);
1510 $desc .= "</programlisting>\n";
1513 $desc .= &MakeDeprecationNote($symbol);
1515 my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1517 if (defined ($SymbolDocs{$symbol})) {
1518 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1519 $desc .= $symbol_docs;
1522 $desc .= $parameters;
1523 $desc .= OutputSymbolTraits ($symbol);
1524 $desc .= "</refsect2>\n";
1525 return ($synop, $desc);
1529 #############################################################################
1530 # Function : OutputTypedef
1531 # Description : Returns the synopsis and detailed description of a typedef.
1532 # Arguments : $symbol - the typedef.
1533 # $declaration - the declaration of the typedef,
1534 # e.g. 'typedef unsigned int guint;'
1535 #############################################################################
1538 my ($symbol, $declaration) = @_;
1539 my $id = &CreateValidSGMLID ($symbol);
1540 my $condition = &MakeConditionDescription ($symbol);
1541 my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1542 my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1544 $desc .= MakeIndexterms($symbol, $id);
1546 $desc .= OutputSymbolExtraLinks($symbol);
1548 if (!defined ($DeclarationConditional{$symbol})) {
1549 my $decl_out = &CreateValidSGML ($declaration);
1550 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1553 $desc .= &MakeDeprecationNote($symbol);
1555 if (defined ($SymbolDocs{$symbol})) {
1556 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1558 $desc .= OutputSymbolTraits ($symbol);
1559 $desc .= "</refsect2>\n";
1560 return ($synop, $desc);
1564 #############################################################################
1565 # Function : OutputStruct
1566 # Description : Returns the synopsis and detailed description of a struct.
1567 # We check if it is a object struct, and if so we only output
1568 # parts of it that are noted as public fields.
1569 # We also use a different IDs for object structs, since the
1570 # original ID is used for the entire RefEntry.
1571 # Arguments : $symbol - the struct.
1572 # $declaration - the declaration of the struct.
1573 #############################################################################
1576 my ($symbol, $declaration) = @_;
1579 my $default_to_public = 1;
1580 if (&CheckIsObject ($symbol)) {
1581 @TRACE@("Found struct gtype: $symbol\n");
1583 $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1589 $id = &CreateValidSGMLID ($symbol . "_struct");
1590 $condition = &MakeConditionDescription ($symbol . "_struct");
1592 $id = &CreateValidSGMLID ($symbol);
1593 $condition = &MakeConditionDescription ($symbol);
1596 # Determine if it is a simple struct or it also has a typedef.
1597 my $has_typedef = 0;
1598 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1605 # For structs with typedefs we just output the struct name.
1607 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1609 $type_output = "struct";
1610 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1612 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1614 $desc .= MakeIndexterms($symbol, $id);
1616 $desc .= OutputSymbolExtraLinks($symbol);
1618 # Form a pretty-printed, private-data-removed form of the declaration
1621 if ($declaration =~ m/^\s*$/) {
1622 @TRACE@("Found opaque struct: $symbol\n");
1623 $decl_out = "typedef struct _$symbol $symbol;";
1624 } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1625 @TRACE@("Found opaque struct: $symbol\n");
1626 $decl_out = "struct $symbol;";
1628 my $public = $default_to_public;
1629 my $new_declaration = "";
1631 my $decl = $declaration;
1633 if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1634 my $struct_contents = $2;
1636 foreach $decl_line (split (/\n/, $struct_contents)) {
1637 @TRACE@("Struct line: $decl_line\n");
1638 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1640 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1643 $new_declaration .= $decl_line . "\n";
1647 if ($new_declaration) {
1648 # Strip any blank lines off the ends.
1649 $new_declaration =~ s/^\s*\n//;
1650 $new_declaration =~ s/\n\s*$/\n/;
1653 $decl_out = "typedef struct {\n" . $new_declaration
1656 $decl_out = "struct $symbol {\n" . $new_declaration
1661 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1662 "Couldn't parse struct:\n$declaration");
1665 # If we couldn't parse the struct or it was all private, output an
1666 # empty struct declaration.
1667 if ($decl_out eq "") {
1669 $decl_out = "typedef struct _$symbol $symbol;";
1671 $decl_out = "struct $symbol;";
1676 $decl_out = &CreateValidSGML ($decl_out);
1677 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1679 $desc .= &MakeDeprecationNote($symbol);
1681 if (defined ($SymbolDocs{$symbol})) {
1682 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1685 # Create a table of fields and descriptions
1687 # FIXME: Inserting  's into the produced type declarations here would
1688 # improve the output in most situations ... except for function
1689 # members of structs!
1690 my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1693 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1695 my $params = $SymbolParams{$symbol};
1697 # If no parameters are filled in, we don't generate the description
1698 # table, for backwards compatibility.
1701 if (defined $params) {
1702 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1703 if ($params->[$i] =~ /\S/) {
1711 my %field_descrs = @$params;
1712 my $missing_parameters = "";
1713 my $unused_parameters = "";
1714 my $id = &CreateValidSGMLID ("$symbol".".members");
1717 <refsect3 id="$id" role="struct_members">\n<title>Members</title>
1718 <informaltable role="struct_members_table" pgwide="1" frame="none">
1720 <colspec colname="struct_members_name" colwidth="300px"/>
1721 <colspec colname="struct_members_description"/>
1722 <colspec colname="struct_members_annotations" colwidth="200px"/>
1727 my $field_name = shift @fields;
1728 my $text = shift @fields;
1729 my $field_descr = $field_descrs{$field_name};
1730 my $param_annotations = "";
1732 $desc .= "<row role=\"member\"><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1733 if (defined $field_descr) {
1734 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1735 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1737 $field_descr =~ s/^(\s|\n)+//msg;
1738 $field_descr =~ s/(\s|\n)+$//msg;
1739 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1740 delete $field_descrs{$field_name};
1742 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1743 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1744 if ($missing_parameters ne "") {
1745 $missing_parameters .= ", ".$field_name;
1747 $missing_parameters = $field_name;
1749 $desc .= "<entry /><entry />\n";
1751 $desc .= "</row>\n";
1753 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1754 foreach my $field_name (keys %field_descrs) {
1755 # Documenting those standard fields is not required anymore, but
1756 # we don't want to warn if they are documented anyway.
1757 if ($field_name =~ /(g_iface|parent_instance|parent_class)/) {
1760 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1761 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1762 if ($unused_parameters ne "") {
1763 $unused_parameters .= ", ".$field_name;
1765 $unused_parameters = $field_name;
1769 # remember missing/unused parameters (needed in tmpl-free build)
1770 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1771 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1773 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1774 $AllUnusedSymbols{$symbol}=$unused_parameters;
1778 if (scalar(@fields) > 0) {
1779 if (! exists ($AllIncompleteSymbols{$symbol})) {
1780 $AllIncompleteSymbols{$symbol}="<items>";
1781 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1782 "Field descriptions for struct $symbol are missing in source code comment block.");
1783 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1788 $desc .= OutputSymbolTraits ($symbol);
1789 $desc .= "</refsect2>\n";
1790 return ($synop, $desc);
1794 #############################################################################
1795 # Function : OutputUnion
1796 # Description : Returns the synopsis and detailed description of a union.
1797 # Arguments : $symbol - the union.
1798 # $declaration - the declaration of the union.
1799 #############################################################################
1802 my ($symbol, $declaration) = @_;
1805 if (&CheckIsObject ($symbol)) {
1806 @TRACE@("Found union gtype: $symbol\n");
1813 $id = &CreateValidSGMLID ($symbol . "_union");
1814 $condition = &MakeConditionDescription ($symbol . "_union");
1816 $id = &CreateValidSGMLID ($symbol);
1817 $condition = &MakeConditionDescription ($symbol);
1820 # Determine if it is a simple struct or it also has a typedef.
1821 my $has_typedef = 0;
1822 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1829 # For unions with typedefs we just output the union name.
1831 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1833 $type_output = "union";
1834 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1836 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1838 $desc .= MakeIndexterms($symbol, $id);
1840 $desc .= OutputSymbolExtraLinks($symbol);
1841 $desc .= &MakeDeprecationNote($symbol);
1843 if (defined ($SymbolDocs{$symbol})) {
1844 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1847 # Create a table of fields and descriptions
1849 # FIXME: Inserting  's into the produced type declarations here would
1850 # improve the output in most situations ... except for function
1851 # members of structs!
1852 my @fields = ParseStructDeclaration($declaration, 0,
1855 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1857 my $params = $SymbolParams{$symbol};
1859 # If no parameters are filled in, we don't generate the description
1860 # table, for backwards compatibility
1863 if (defined $params) {
1864 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1865 if ($params->[$i] =~ /\S/) {
1873 my %field_descrs = @$params;
1874 my $missing_parameters = "";
1875 my $unused_parameters = "";
1876 my $id = &CreateValidSGMLID ("$symbol".".members");
1879 <refsect3 id="$id" role="union_members">\n<title>Members</title>
1880 <informaltable role="union_members_table" pgwide="1" frame="none">
1882 <colspec colname="union_members_name" colwidth="300px"/>
1883 <colspec colname="union_members_description"/>
1884 <colspec colname="union_members_annotations" colwidth="200px"/>
1889 my $field_name = shift @fields;
1890 my $text = shift @fields;
1891 my $field_descr = $field_descrs{$field_name};
1892 my $param_annotations = "";
1894 $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1895 if (defined $field_descr) {
1896 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1897 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1900 $field_descr =~ s/^(\s|\n)+//msg;
1901 $field_descr =~ s/(\s|\n)+$//msg;
1902 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1903 delete $field_descrs{$field_name};
1905 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1906 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1907 if ($missing_parameters ne "") {
1908 $missing_parameters .= ", ".$field_name;
1910 $missing_parameters = $field_name;
1912 $desc .= "<entry /><entry />\n";
1914 $desc .= "</row>\n";
1916 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1917 foreach my $field_name (keys %field_descrs) {
1918 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1919 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1920 if ($unused_parameters ne "") {
1921 $unused_parameters .= ", ".$field_name;
1923 $unused_parameters = $field_name;
1927 # remember missing/unused parameters (needed in tmpl-free build)
1928 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1929 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1931 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1932 $AllUnusedSymbols{$symbol}=$unused_parameters;
1936 if (scalar(@fields) > 0) {
1937 if (! exists ($AllIncompleteSymbols{$symbol})) {
1938 $AllIncompleteSymbols{$symbol}="<items>";
1939 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1940 "Field descriptions for union $symbol are missing in source code comment block.");
1941 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1946 $desc .= OutputSymbolTraits ($symbol);
1947 $desc .= "</refsect2>\n";
1948 return ($synop, $desc);
1952 #############################################################################
1953 # Function : OutputEnum
1954 # Description : Returns the synopsis and detailed description of a enum.
1955 # Arguments : $symbol - the enum.
1956 # $declaration - the declaration of the enum.
1957 #############################################################################
1960 my ($symbol, $declaration) = @_;
1963 if (&CheckIsObject ($symbol)) {
1964 @TRACE@("Found enum gtype: $symbol\n");
1971 $id = &CreateValidSGMLID ($symbol . "_enum");
1972 $condition = &MakeConditionDescription ($symbol . "_enum");
1974 $id = &CreateValidSGMLID ($symbol);
1975 $condition = &MakeConditionDescription ($symbol);
1978 my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1979 my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1981 $desc .= MakeIndexterms($symbol, $id);
1983 $desc .= OutputSymbolExtraLinks($symbol);
1984 $desc .= &MakeDeprecationNote($symbol);
1986 if (defined ($SymbolDocs{$symbol})) {
1987 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1990 # Create a table of fields and descriptions
1992 my @fields = ParseEnumDeclaration($declaration);
1993 my $params = $SymbolParams{$symbol};
1995 # If nothing at all is documented log a single summary warning at the end.
1996 # Otherwise, warn about each undocumented item.
1999 if (defined $params) {
2000 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
2001 if ($params->[$i] =~ /\S/) {
2008 my %field_descrs = (defined $params ? @$params : ());
2009 my $missing_parameters = "";
2010 my $unused_parameters = "";
2012 $id = &CreateValidSGMLID ("$symbol".".members");
2014 <refsect3 id="$id" role="enum_members">\n<title>Members</title>
2015 <informaltable role="enum_members_table" pgwide="1" frame="none">
2017 <colspec colname="enum_members_name" colwidth="300px"/>
2018 <colspec colname="enum_members_description"/>
2019 <colspec colname="enum_members_annotations" colwidth="200px"/>
2023 for my $field_name (@fields) {
2024 my $field_descr = $field_descrs{$field_name};
2025 my $param_annotations = "";
2027 $id = &CreateValidSGMLID ($field_name);
2028 $condition = &MakeConditionDescription ($field_name);
2029 $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2030 if (defined $field_descr) {
2031 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2032 $field_descr = &ConvertMarkDown($symbol, $field_descr);
2033 $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2034 delete $field_descrs{$field_name};
2037 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2038 "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2039 if ($missing_parameters ne "") {
2040 $missing_parameters .= ", ".$field_name;
2042 $missing_parameters = $field_name;
2045 $desc .= "<entry /><entry />\n";
2047 $desc .= "</row>\n";
2049 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2050 foreach my $field_name (keys %field_descrs) {
2051 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2052 "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2053 if ($unused_parameters ne "") {
2054 $unused_parameters .= ", ".$field_name;
2056 $unused_parameters = $field_name;
2060 # remember missing/unused parameters (needed in tmpl-free build)
2061 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2062 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2064 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2065 $AllUnusedSymbols{$symbol}=$unused_parameters;
2069 if (scalar(@fields) > 0) {
2070 if (! exists ($AllIncompleteSymbols{$symbol})) {
2071 $AllIncompleteSymbols{$symbol}="<items>";
2072 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2073 "Value descriptions for $symbol are missing in source code comment block.");
2078 $desc .= OutputSymbolTraits ($symbol);
2079 $desc .= "</refsect2>\n";
2080 return ($synop, $desc);
2084 #############################################################################
2085 # Function : OutputVariable
2086 # Description : Returns the synopsis and detailed description of a variable.
2087 # Arguments : $symbol - the extern'ed variable.
2088 # $declaration - the declaration of the variable.
2089 #############################################################################
2091 sub OutputVariable {
2092 my ($symbol, $declaration) = @_;
2093 my $id = &CreateValidSGMLID ($symbol);
2094 my $condition = &MakeConditionDescription ($symbol);
2096 @TRACE@("ouputing variable: '$symbol' '$declaration'");
2099 if ($declaration =~ m/^\s*extern\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*;/) {
2100 my $mod1 = defined ($1) ? $1 : "";
2101 my $ptr = defined ($3) ? $3 : "";
2102 my $space = defined ($4) ? $4 : "";
2103 my $mod2 = defined ($5) ? $5 : "";
2104 $type_output = "extern $mod1$ptr$space$mod2";
2105 } elsif ($declaration =~ m/^\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=/) {
2106 my $mod1 = defined ($1) ? $1 : "";
2107 my $ptr = defined ($3) ? $3 : "";
2108 my $space = defined ($4) ? $4 : "";
2109 my $mod2 = defined ($5) ? $5 : "";
2110 $type_output = "$mod1$ptr$space$mod2";
2112 $type_output = "extern";
2114 my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2116 my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2118 $desc .= MakeIndexterms($symbol, $id);
2120 $desc .= OutputSymbolExtraLinks($symbol);
2122 my $decl_out = &CreateValidSGML ($declaration);
2123 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2125 $desc .= &MakeDeprecationNote($symbol);
2127 if (defined ($SymbolDocs{$symbol})) {
2128 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2130 if (defined ($SymbolAnnotations{$symbol})) {
2131 my $param_desc = $SymbolAnnotations{$symbol};
2132 my $param_annotations = "";
2133 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2134 if ($param_annotations ne "") {
2135 $desc .= "\n<para>$param_annotations</para>";
2139 $desc .= OutputSymbolTraits ($symbol);
2140 $desc .= "</refsect2>\n";
2141 return ($synop, $desc);
2145 #############################################################################
2146 # Function : OutputFunction
2147 # Description : Returns the synopsis and detailed description of a function.
2148 # Arguments : $symbol - the function.
2149 # $declaration - the declaration of the function.
2150 #############################################################################
2152 sub OutputFunction {
2153 my ($symbol, $declaration, $symbol_type) = @_;
2154 my $id = &CreateValidSGMLID ($symbol);
2155 my $condition = &MakeConditionDescription ($symbol);
2157 # Take out the return type $1 $2 $3
2158 $declaration =~ s/<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n//;
2159 my $type_modifier = defined($1) ? $1 : "";
2162 # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2163 $pointer =~ s/\s+$//;
2164 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2166 #if ($symbol_type eq 'USER_FUNCTION') {
2167 # $start = "typedef ";
2170 # We output const rather than G_CONST_RETURN.
2171 $type_modifier =~ s/G_CONST_RETURN/const/g;
2172 $pointer =~ s/G_CONST_RETURN/const/g;
2173 $pointer =~ s/^\s+/ /g;
2175 my $ret_type_output;
2176 $ret_type_output = "$start$type_modifier$xref$pointer\n";
2179 $indent_len = length ($symbol) + 2;
2180 my $char1 = my $char2 = my $char3 = "";
2181 if ($symbol_type eq 'USER_FUNCTION') {
2183 $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2185 $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2188 my ($symbol_output, $symbol_desc_output);
2189 $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2190 if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2191 $symbol_desc_output = "$char1$char2$symbol$char3 ";
2193 $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2194 $symbol_desc_output = "$char1$char2$symbol$char3\n"
2195 . (' ' x ($indent_len - 1));
2198 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";
2200 my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol} ()</title>\n";
2202 $desc .= MakeIndexterms($symbol, $id);
2204 $desc .= OutputSymbolExtraLinks($symbol);
2206 $desc .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2208 my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2210 &tagify($_[0],"parameter");
2213 for (my $i = 1; $i <= $#fields; $i += 2) {
2214 my $field_name = $fields[$i];
2217 $desc .= "$field_name";
2220 . (' ' x $indent_len)
2226 $desc .= ");</programlisting>\n";
2228 $desc .= &MakeDeprecationNote($symbol);
2230 if (defined ($SymbolDocs{$symbol})) {
2231 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2233 if (defined ($SymbolAnnotations{$symbol})) {
2234 my $param_desc = $SymbolAnnotations{$symbol};
2235 my $param_annotations = "";
2236 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2237 if ($param_annotations ne "") {
2238 $desc .= "\n<para>$param_annotations</para>";
2242 $desc .= &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2243 $desc .= OutputSymbolTraits ($symbol);
2244 $desc .= "</refsect2>\n";
2245 return ($synop, $desc);
2249 #############################################################################
2250 # Function : OutputParamDescriptions
2251 # Description : Returns the DocBook output describing the parameters of a
2252 # function, macro or signal handler.
2253 # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2254 # handlers have an implicit user_data parameter last.
2255 # $symbol - the name of the function/macro being described.
2256 # @fields - parsed fields from the declaration, used to determine
2257 # undocumented/unused entries
2258 #############################################################################
2260 sub OutputParamDescriptions {
2261 my ($symbol_type, $symbol, @fields) = @_;
2263 my $params = $SymbolParams{$symbol};
2265 my %field_descrs = ();
2268 %field_descrs = @fields;
2269 delete $field_descrs{"void"};
2270 delete $field_descrs{"Returns"};
2273 if (defined $params) {
2275 my $params_desc = "";
2276 my $missing_parameters = "";
2277 my $unused_parameters = "";
2280 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2281 my $param_name = $$params[$j];
2282 my $param_desc = $$params[$j + 1];
2283 my $param_annotations = "";
2285 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2286 $param_desc = &ConvertMarkDown($symbol, $param_desc);
2288 $param_desc =~ s/^(\s|\n)+//msg;
2289 $param_desc =~ s/(\s|\n)+$//msg;
2290 if ($param_name eq "Returns") {
2291 $returns = $param_desc;
2292 if ($param_annotations ne "") {
2293 $returns .= "\n<para>$param_annotations</para>";
2295 } elsif ($param_name eq "void") {
2296 # FIXME: &LogWarning()?
2297 @TRACE@("!!!! void in params for $symbol?\n");
2300 if (!defined $field_descrs{$param_name}) {
2301 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2302 "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2303 if ($unused_parameters ne "") {
2304 $unused_parameters .= ", ".$param_name;
2306 $unused_parameters = $param_name;
2309 delete $field_descrs{$param_name};
2312 if($param_desc ne "") {
2313 $params_desc .= "<row><entry role=\"parameter_name\"><para>$param_name</para></entry>\n<entry role=\"parameter_description\">$param_desc</entry>\n<entry role=\"parameter_annotations\">$param_annotations</entry></row>\n";
2318 foreach my $param_name (keys %field_descrs) {
2319 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2320 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2321 if ($missing_parameters ne "") {
2322 $missing_parameters .= ", ".$param_name;
2324 $missing_parameters = $param_name;
2328 # Signals have an implicit user_data parameter which we describe.
2329 if ($symbol_type eq "SIGNAL") {
2330 $params_desc .= "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry role=\"parameter_description\"><simpara>user data set when the signal handler was connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n";
2333 # Start a table if we need one.
2334 if ($params_desc ne "") {
2335 my $id = &CreateValidSGMLID ("$symbol".".parameters");
2338 <refsect3 id="$id" role="parameters">\n<title>Parameters</title>
2339 <informaltable role="parameters_table" pgwide="1" frame="none">
2341 <colspec colname="parameters_name" colwidth="150px"/>
2342 <colspec colname="parameters_description"/>
2343 <colspec colname="parameters_annotations" colwidth="200px"/>
2346 $output .= $params_desc;
2347 $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2350 # Output the returns info last
2351 if ($returns ne "") {
2352 my $id = &CreateValidSGMLID ("$symbol".".returns");
2355 <refsect3 id="$id" role=\"returns\">\n<title>Returns</title>
2357 $output .= $returns;
2358 $output .= "\n</refsect3>";
2361 # remember missing/unused parameters (needed in tmpl-free build)
2362 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2363 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2365 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2366 $AllUnusedSymbols{$symbol}=$unused_parameters;
2369 if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2370 if (! exists ($AllIncompleteSymbols{$symbol})) {
2371 $AllIncompleteSymbols{$symbol}="<parameters>";
2379 #############################################################################
2380 # Function : ParseStabilityLevel
2381 # Description : Parses a stability level and outputs a warning if it isn't
2383 # Arguments : $stability - the stability text.
2384 # $file, $line - context for error message
2385 # $message - description of where the level is from, to use in
2386 # any error message.
2387 # Returns : The parsed stability level string.
2388 #############################################################################
2390 sub ParseStabilityLevel {
2391 my ($stability, $file, $line, $message) = @_;
2393 $stability =~ s/^\s*//;
2394 $stability =~ s/\s*$//;
2395 if ($stability =~ m/^stable$/i) {
2396 $stability = "Stable";
2397 } elsif ($stability =~ m/^unstable$/i) {
2398 $stability = "Unstable";
2399 } elsif ($stability =~ m/^private$/i) {
2400 $stability = "Private";
2402 &LogWarning ($file, $line, "$message is $stability.".
2403 "It should be one of these: Stable, Unstable, or Private.");
2409 #############################################################################
2410 # Function : OutputDBFile
2411 # Description : Outputs the final DocBook file for one section.
2412 # Arguments : $file - the name of the file.
2413 # $title - the title from the $MODULE-sections.txt file, which
2414 # will be overridden by the title in the template file.
2415 # $section_id - the id to use for the toplevel tag.
2416 # $includes - comma-separates list of include files added at top of
2417 # synopsis, with '<' '>' around them (if not already enclosed in "").
2418 # $functions_synop - reference to the DocBook for the Functions Synopsis part.
2419 # $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2420 # $functions_details - reference to the DocBook for the Functions Details part.
2421 # $other_details - reference to the DocBook for the Types and Values Details part.
2422 # $signal_synop - reference to the DocBook for the Signal Synopsis part
2423 # $signal_desc - reference to the DocBook for the Signal Description part
2424 # $args_synop - reference to the DocBook for the Arg Synopsis part
2425 # $args_desc - reference to the DocBook for the Arg Description part
2426 # $hierarchy - reference to the DocBook for the Object Hierarchy part
2427 # $interfaces - reference to the DocBook for the Interfaces part
2428 # $implementations - reference to the DocBook for the Known Implementations part
2429 # $prerequisites - reference to the DocBook for the Prerequisites part
2430 # $derived - reference to the DocBook for the Derived Interfaces part
2431 # $file_objects - reference to an array of objects in this file
2432 #############################################################################
2435 my ($file, $title, $section_id, $includes, $functions_synop, $other_synop, $functions_details, $other_details, $signals_synop, $signals_desc, $args_synop, $args_desc, $hierarchy, $interfaces, $implementations, $prerequisites, $derived, $file_objects) = @_;
2437 @TRACE@("Output docbook for file $file with title '$title'\n");
2439 # The edited title overrides the one from the sections file.
2440 my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2441 if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2442 $title = $new_title;
2443 @TRACE@("Found title: $title\n");
2445 my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2446 if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2449 # Don't use ConvertMarkDown here for now since we don't want blocks
2450 $short_desc = &ExpandAbbreviations("$title:Short_description",
2452 @TRACE@("Found short_desc: $short_desc");
2454 my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2455 if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2458 $long_desc = &ConvertMarkDown("$title:Long_description",
2460 @TRACE@("Found long_desc: $long_desc");
2462 my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2463 if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2466 $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2467 @TRACE@("Found see_also: $see_also");
2470 $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2472 my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2473 if (!defined ($stability) || $stability =~ m/^\s*$/) {
2476 $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2477 @TRACE@("Found stability: $stability");
2480 $AnnotationsUsed{$stability} = 1;
2481 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2482 } elsif ($DEFAULT_STABILITY) {
2483 $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2484 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2487 my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2488 if (!defined ($image) || $image =~ m/^\s*$/) {
2496 if ($image =~ /jpe?g$/i) {
2497 $format = "format='JPEG'";
2498 } elsif ($image =~ /png$/i) {
2499 $format = "format='PNG'";
2500 } elsif ($image =~ /svg$/i) {
2501 $format = "format='SVG'";
2506 $image = " <inlinegraphic fileref='$image' $format/>\n"
2509 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2511 my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2514 my $include_output = "";
2516 $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2518 foreach $include (split (/,/, $includes)) {
2519 if ($include =~ m/^\".+\"$/) {
2520 $include_output .= "#include ${include}\n";
2523 $include =~ s/^\s+|\s+$//gs;
2524 $include_output .= "#include <${include}>\n";
2527 $include_output .= "</synopsis></refsect1>\n";
2530 my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2532 my $old_db_file = "$DB_OUTPUT_DIR/$file.xml";
2533 my $new_db_file = "$DB_OUTPUT_DIR/$file.xml.new";
2535 open (OUTPUT, ">$new_db_file")
2536 || die "Can't create $new_db_file: $!";
2538 my $object_anchors = "";
2539 foreach my $object (@$file_objects) {
2540 next if ($object eq $section_id);
2541 my $id = CreateValidSGMLID($object);
2542 @TRACE@("Adding anchor for $object\n");
2543 $object_anchors .= "<anchor id=\"$id\"/>";
2546 # Make sure we produce valid docbook
2547 $$functions_details ||= "<para />";
2549 # We used to output this, but is messes up our UpdateFileIfChanged code
2550 # since it changes every day (and it is only used in the man pages):
2551 # "<refentry id="$section_id" revision="$mday $month $year">"
2554 ${\( MakeDocHeader ("refentry") )}
2555 <refentry id="$section_id">
2557 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2558 <manvolnum>3</manvolnum>
2559 <refmiscinfo>\U$MODULE\E Library$image</refmiscinfo>
2562 <refname>$title</refname>
2563 <refpurpose>$short_desc</refpurpose>
2566 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2568 <refsect1 id="$section_id.description" role="desc">
2569 <title role="desc.title">Description</title>
2570 $extralinks$long_desc
2572 <refsect1 id="$section_id.functions_details" role="details">
2573 <title role="details.title">Functions</title>
2576 <refsect1 id="$section_id.other_details" role="details">
2577 <title role="details.title">Types and Values</title>
2580 $$args_desc$$signals_desc$see_also
2585 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2589 #############################################################################
2590 # Function : OutputExtraFile
2591 # Description : Copies an "extra" DocBook file into the output directory,
2592 # expanding abbreviations
2593 # Arguments : $file - the source file.
2594 #############################################################################
2595 sub OutputExtraFile {
2600 ($basename = $file) =~ s!^.*/!!;
2602 my $old_db_file = "$DB_OUTPUT_DIR/$basename";
2603 my $new_db_file = "$DB_OUTPUT_DIR/$basename.new";
2607 open(EXTRA_FILE, "<$file") || die "Can't open $file";
2611 $contents = <EXTRA_FILE>;
2614 open (OUTPUT, ">$new_db_file")
2615 || die "Can't create $new_db_file: $!";
2617 print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2620 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2622 #############################################################################
2623 # Function : OutputBook
2624 # Description : Outputs the entities that need to be included into the
2625 # main docbook file for the module.
2626 # Arguments : $book_top - the declarations of the entities, which are added
2627 # at the top of the main docbook file.
2628 # $book_bottom - the references to the entities, which are
2629 # added in the main docbook file at the desired position.
2630 #############################################################################
2633 my ($book_top, $book_bottom) = @_;
2635 my $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.top";
2636 my $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.top.new";
2638 open (OUTPUT, ">$new_file")
2639 || die "Can't create $new_file: $!";
2640 print OUTPUT $book_top;
2643 &UpdateFileIfChanged ($old_file, $new_file, 0);
2646 $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom";
2647 $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom.new";
2649 open (OUTPUT, ">$new_file")
2650 || die "Can't create $new_file: $!";
2651 print OUTPUT $book_bottom;
2654 &UpdateFileIfChanged ($old_file, $new_file, 0);
2657 # If the main docbook file hasn't been created yet, we create it here.
2658 # The user can tweak it later.
2659 if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2660 open (OUTPUT, ">$MAIN_SGML_FILE")
2661 || die "Can't create $MAIN_SGML_FILE: $!";
2664 ${\( MakeDocHeader ("book") )}
2667 <title>&package_name; Reference Manual</title>
2669 for &package_string;.
2670 The latest version of this documentation can be found on-line at
2671 <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2676 <title>[Insert title here]</title>
2680 if (-e $OBJECT_TREE_FILE) {
2682 <chapter id="object-tree">
2683 <title>Object Hierarchy</title>
2684 <xi:include href="xml/tree_index.sgml"/>
2689 <!-- enable this when you use gobject types
2690 <chapter id="object-tree">
2691 <title>Object Hierarchy</title>
2692 <xi:include href="xml/tree_index.sgml"/>
2698 <index id="api-index-full">
2699 <title>API Index</title>
2700 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2702 <index id="deprecated-api-index" role="deprecated">
2703 <title>Index of deprecated API</title>
2704 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2707 if (keys(%AnnotationsUsed)) {
2709 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2713 <!-- enable this when you use gobject introspection annotations
2714 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2727 #############################################################################
2728 # Function : CreateValidSGML
2729 # Description : This turns any chars which are used in SGML into entities,
2730 # e.g. '<' into '<'
2731 # Arguments : $text - the text to turn into proper SGML.
2732 #############################################################################
2734 sub CreateValidSGML {
2736 $text =~ s/&/&/g; # Do this first, or the others get messed up.
2737 $text =~ s/</</g;
2738 $text =~ s/>/>/g;
2739 # browers render single tabs inconsistently
2740 $text =~ s/([^\s])\t([^\s])/$1 $2/g;
2744 #############################################################################
2745 # Function : ConvertSGMLChars
2746 # Description : This is used for text in source code comment blocks, to turn
2747 # chars which are used in SGML into entities, e.g. '<' into
2748 # '<'. Depending on $INLINE_MARKUP_MODE, this is done
2749 # unconditionally or only if the character doesn't seem to be
2750 # part of an SGML construct (tag or entity reference).
2751 # Arguments : $text - the text to turn into proper SGML.
2752 #############################################################################
2754 sub ConvertSGMLChars {
2755 my ($symbol, $text) = @_;
2757 if ($INLINE_MARKUP_MODE) {
2758 # For the XML/SGML mode only convert to entities outside CDATA sections.
2759 return &ModifyXMLElements ($text, $symbol,
2760 "<!\\[CDATA\\[|<programlisting[^>]*>",
2761 \&ConvertSGMLCharsEndTag,
2762 \&ConvertSGMLCharsCallback);
2764 # For the simple non-sgml mode, convert to entities everywhere.
2766 # First, convert freestanding & to &
2767 $text =~ s/&(?![a-zA-Z#]+;)/&/g;
2768 $text =~ s/</</g;
2769 # Allow ">" at beginning of string for blockquote markdown
2770 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2777 sub ConvertSGMLCharsEndTag {
2778 if ($_[0] eq "<!\[CDATA\[") {
2781 return "</programlisting>";
2785 sub ConvertSGMLCharsCallback {
2786 my ($text, $symbol, $tag) = @_;
2788 if ($tag =~ m/^<programlisting/) {
2789 # We can handle <programlisting> specially here.
2790 return &ModifyXMLElements ($text, $symbol,
2792 \&ConvertSGMLCharsEndTag,
2793 \&ConvertSGMLCharsCallback2);
2794 } elsif ($tag eq "") {
2795 # If we're not in CDATA convert to entities.
2796 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2797 $text =~ s/<(?![a-zA-Z\/!])/</g;
2798 # Allow ">" at beginning of string for blockquote markdown
2799 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2801 # Handle "#include <xxxxx>"
2802 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2808 sub ConvertSGMLCharsCallback2 {
2809 my ($text, $symbol, $tag) = @_;
2811 # If we're not in CDATA convert to entities.
2812 # We could handle <programlisting> differently, though I'm not sure it helps.
2814 # replace only if its not a tag
2815 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2816 $text =~ s/<(?![a-zA-Z\/!])/</g;
2817 $text =~ s/(?<![a-zA-Z0-9"'\/-])>/>/g;
2819 # Handle "#include <xxxxx>"
2820 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2826 #############################################################################
2827 # Function : ExpandAnnotation
2828 # Description : This turns annotations into acronym tags.
2829 # Arguments : $symbol - the symbol being documented, for error messages.
2830 # $text - the text to expand.
2831 #############################################################################
2832 sub ExpandAnnotation {
2833 my ($symbol, $param_desc) = @_;
2834 my $param_annotations = "";
2836 # look for annotations at the start of the comment part
2837 # function level annotations don't end with a colon ':'
2838 if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
2843 @annotations = split(/\)\s*\(/,$1);
2844 @TRACE@("annotations for $symbol: '$1'\n");
2845 foreach $annotation (@annotations) {
2846 # need to search for the longest key-match in %AnnotationDefinition
2848 my $match_annotation="";
2850 foreach $annotationdef (keys %AnnotationDefinition) {
2851 if ($annotation =~ m/^$annotationdef/) {
2852 if (length($annotationdef)>$match_length) {
2853 $match_length=length($annotationdef);
2854 $match_annotation=$annotationdef;
2858 my $annotation_extra = "";
2859 if ($match_annotation ne "") {
2860 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2861 $annotation_extra = " $1";
2863 $AnnotationsUsed{$match_annotation} = 1;
2864 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2867 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2868 "unknown annotation \"$annotation\" in documentation for $symbol.");
2869 $param_annotations .= "[$annotation]";
2873 $param_desc =~ m/^(.*?)\.*\s*$/s;
2874 $param_desc = "$1. ";
2876 if ($param_annotations ne "") {
2877 $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2879 return ($param_desc, $param_annotations);
2882 #############################################################################
2883 # Function : ExpandAbbreviations
2884 # Description : This turns the abbreviations function(), macro(), @param,
2885 # %constant, and #symbol into appropriate DocBook markup.
2886 # CDATA sections and <programlisting> parts are skipped.
2887 # Arguments : $symbol - the symbol being documented, for error messages.
2888 # $text - the text to expand.
2889 #############################################################################
2891 sub ExpandAbbreviations {
2892 my ($symbol, $text) = @_;
2894 # Note: This is a fallback and normally done in the markdown parser
2896 # Convert "|[" and "]|" into the start and end of program listing examples.
2897 # Support \[<!-- language="C" --> modifiers
2898 $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2899 $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2900 $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2902 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2904 return &ModifyXMLElements ($text, $symbol,
2905 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2906 \&ExpandAbbreviationsEndTag,
2907 \&ExpandAbbreviationsCallback);
2911 # Returns the end tag (as a regexp) corresponding to the given start tag.
2912 sub ExpandAbbreviationsEndTag {
2913 my ($start_tag) = @_;
2915 if ($start_tag eq "<!\[CDATA\[") {
2917 } elsif ($start_tag eq "<!DOCTYPE") {
2919 } elsif ($start_tag =~ m/<(\w+)/) {
2924 # Called inside or outside each CDATA or <programlisting> section.
2925 sub ExpandAbbreviationsCallback {
2926 my ($text, $symbol, $tag) = @_;
2928 if ($tag =~ m/^<programlisting/) {
2929 # Handle any embedded CDATA sections.
2930 return &ModifyXMLElements ($text, $symbol,
2932 \&ExpandAbbreviationsEndTag,
2933 \&ExpandAbbreviationsCallback2);
2934 } elsif ($tag eq "") {
2935 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2937 # We are outside any CDATA or <programlisting> sections, so we expand
2938 # any gtk-doc abbreviations.
2940 # Convert '@param()'
2941 # FIXME: we could make those also links ($symbol.$2), but that would be less
2942 # useful as the link target is a few lines up or down
2943 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2945 # Convert 'function()' or 'macro()'.
2946 # if there is abc_*_def() we don't want to make a link to _def()
2947 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2948 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2949 # handle #Object.func()
2950 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2952 # Convert '@param', but not '\@param'.
2953 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2954 $text =~ s/\\\@/\@/g;
2956 # Convert '%constant', but not '\%constant'.
2957 # Also allow negative numbers, e.g. %-1.
2958 $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2959 $text =~ s/\\\%/\%/g;
2961 # Convert '#symbol', but not '\#symbol'.
2962 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2969 # This is called inside a <programlisting>
2970 sub ExpandAbbreviationsCallback2 {
2971 my ($text, $symbol, $tag) = @_;
2974 # We are inside a <programlisting> but outside any CDATA sections,
2975 # so we expand any gtk-doc abbreviations.
2976 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2977 # why not just call it
2978 $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2979 } elsif ($tag eq "<![CDATA[") {
2980 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2981 $text = &ReplaceEntities ($text, $symbol);
2988 my ($symbol, $tag) = @_;;
2991 # Check for things like '#include', '#define', and skip them.
2992 if ($PreProcessorDirectives{$symbol}) {
2996 # Get rid of special suffixes ('-struct','-enum').
2997 $text =~ s/-struct$//;
2998 $text =~ s/-enum$//;
3000 # If the symbol is in the form "Object::signal", then change the symbol to
3001 # "Object-signal" and use "signal" as the text.
3002 if ($symbol =~ s/::/-/) {
3006 # If the symbol is in the form "Object:property", then change the symbol to
3007 # "Object--property" and use "property" as the text.
3008 if ($symbol =~ s/:/--/) {
3013 $text = tagify ($text, $tag);
3016 return &MakeXRef($symbol, $text);
3020 #############################################################################
3021 # Function : ModifyXMLElements
3022 # Description : Looks for given XML element tags within the text, and calls
3023 # the callback on pieces of text inside & outside those elements.
3024 # Used for special handling of text inside things like CDATA
3025 # and <programlisting>.
3026 # Arguments : $text - the text.
3027 # $symbol - the symbol currently being documented (only used for
3029 # $start_tag_regexp - the regular expression to match start tags.
3030 # e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3031 # CDATA sections or programlisting elements.
3032 # $end_tag_func - function which is passed the matched start tag
3033 # and should return the appropriate end tag string regexp.
3034 # $callback - callback called with each part of the text. It is
3035 # called with a piece of text, the symbol being
3036 # documented, and the matched start tag or "" if the text
3037 # is outside the XML elements being matched.
3038 #############################################################################
3039 sub ModifyXMLElements {
3040 my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3041 my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3044 while ($text =~ m/$start_tag_regexp/s) {
3045 $before_tag = $`; # Prematch for last successful match string
3046 $start_tag = $&; # Last successful match
3047 $text = $'; # Postmatch for last successful match string
3049 $result .= &$callback ($before_tag, $symbol, "");
3050 $result .= $start_tag;
3052 # get the matching end-tag for current tag
3053 $end_tag_regexp = &$end_tag_func ($start_tag);
3055 if ($text =~ m/$end_tag_regexp/s) {
3060 $result .= &$callback ($before_tag, $symbol, $start_tag);
3061 $result .= $end_tag;
3063 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3064 "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3065 # Just assume it is all inside the tag.
3066 $result .= &$callback ($text, $symbol, $start_tag);
3071 # Handle any remaining text outside the tags.
3072 $result .= &$callback ($text, $symbol, "");
3081 # Adds a tag around some text.
3082 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3084 my ($text, $elem) = @_;
3085 return "<" . $elem . ">" . $text . "</" . $elem . ">";
3088 #############################################################################
3089 # Function : MakeDocHeader
3090 # Description : Builds a docbook header for the given tag
3091 # Arguments : $tag - doctype tag
3092 #############################################################################
3096 my $header = $doctype_header;
3097 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE $tag/;
3099 # fix the path for book since this is one level up
3100 if ($tag eq "book") {
3101 $header =~ s#<!ENTITY % gtkdocentities SYSTEM \"../([a-zA-Z./]+)\">#<!ENTITY % gtkdocentities SYSTEM \"$1\">#;
3108 #############################################################################
3109 # Function : MakeXRef
3110 # Description : This returns a cross-reference link to the given symbol.
3111 # Though it doesn't try to do this for a few standard C types
3112 # that it knows won't be in the documentation.
3113 # Arguments : $symbol - the symbol to try to create a XRef to.
3114 # $text - text text to put inside the XRef, defaults to $symbol
3115 #############################################################################
3118 my ($symbol, $text) = ($_[0], $_[1]);
3120 $symbol =~ s/^\s+//;
3121 $symbol =~ s/\s+$//;
3123 if (!defined($text)) {
3126 # Get rid of special suffixes ('-struct','-enum').
3127 $text =~ s/-struct$//;
3128 $text =~ s/-enum$//;
3131 if ($symbol =~ m/ /) {
3135 @TRACE@("Getting type link for $symbol -> $text\n");
3137 my $symbol_id = &CreateValidSGMLID ($symbol);
3138 return "<link linkend=\"$symbol_id\">$text</link>";
3142 #############################################################################
3143 # Function : MakeIndexterms
3144 # Description : This returns a indexterm elements for the given symbol
3145 # Arguments : $symbol - the symbol to create indexterms for
3146 #############################################################################
3148 sub MakeIndexterms {
3149 my ($symbol, $id) = @_;
3153 # make the index useful, by ommiting the namespace when sorting
3154 if ($NAME_SPACE ne "") {
3155 if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3156 $sortas=" sortas=\"$1\"";
3160 if (exists $Deprecated{$symbol}) {
3161 $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3162 $IndexEntriesDeprecated{$symbol}=$id;
3163 $IndexEntriesFull{$symbol}=$id;
3165 if (exists $Since{$symbol}) {
3166 my $since = $Since{$symbol};
3170 $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3172 $IndexEntriesSince{$symbol}=$id;
3173 $IndexEntriesFull{$symbol}=$id;
3176 $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3177 $IndexEntriesFull{$symbol}=$id;
3183 #############################################################################
3184 # Function : MakeDeprecationNote
3185 # Description : This returns a deprecation warning for the given symbol.
3186 # Arguments : $symbol - the symbol to try to create a warning for.
3187 #############################################################################
3189 sub MakeDeprecationNote {
3190 my ($symbol) = $_[0];
3192 if (exists $Deprecated{$symbol}) {
3195 $desc .= "<warning><para><literal>$symbol</literal> ";
3197 $note = $Deprecated{$symbol};
3199 if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3200 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3202 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3204 $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3208 $note = &ConvertMarkDown($symbol, $note);
3209 $desc .= " " . $note;
3211 $desc .= "</warning>\n";
3216 #############################################################################
3217 # Function : MakeConditionDescription
3218 # Description : This returns a sumary of conditions for the given symbol.
3219 # Arguments : $symbol - the symbol to try to create the sumary.
3220 #############################################################################
3222 sub MakeConditionDescription {
3223 my ($symbol) = $_[0];
3226 if (exists $Deprecated{$symbol}) {
3231 if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3232 $desc .= "deprecated:$1";
3234 $desc .= "deprecated";
3238 if (exists $Since{$symbol}) {
3243 if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3244 $desc .= "since:$1";
3250 if (exists $StabilityLevel{$symbol}) {
3254 $desc .= "stability:".$StabilityLevel{$symbol};
3259 $cond =~ s/\"/"/g;
3260 $desc=" condition=\"".$cond."\"";
3261 @TRACE@("condition for '$symbol' = '$desc'\n");
3266 #############################################################################
3267 # Function : GetHierarchy
3268 # Description : Returns the DocBook output describing the ancestors and
3269 # immediate children of a GObject subclass. It uses the
3270 # global @Objects and @ObjectLevels arrays to walk the tree.
3272 # Arguments : $object - the GtkObject subclass.
3273 # @hierarchy - previous hierarchy
3274 #############################################################################
3277 my ($object,$hierarchy_ref) = @_;
3278 my @hierarchy = @{$hierarchy_ref};
3280 # Find object in the objects array.
3286 for ($i = 0; $i < @Objects; $i++) {
3288 if ($ObjectLevels[$i] <= $level) {
3291 elsif ($ObjectLevels[$i] == $level + 1) {
3292 push (@children, $Objects[$i]);
3295 elsif ($Objects[$i] eq $object) {
3298 $level = $ObjectLevels[$i];
3305 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3307 push (@ancestors, $object);
3308 @TRACE@("Level: $level\n");
3309 while ($level > 1) {
3311 if ($ObjectLevels[$j] < $level) {
3312 push (@ancestors, $Objects[$j]);
3313 $level = $ObjectLevels[$j];
3314 @TRACE@("Level: $level\n");
3318 # Output the ancestors, indented and with links.
3321 for ($i = $#ancestors; $i >= 0; $i--) {
3324 my $ancestor = $ancestors[$i];
3325 my $ancestor_id = &CreateValidSGMLID ($ancestor);
3326 my $indent = ' ' x ($level * 4);
3327 # Don't add a link to the current object, i.e. when i == 0.
3329 $entry_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3330 $alt_text = $indent . $ancestor;
3332 $entry_text = $indent . $ancestor;
3333 $alt_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3335 @TRACE@("Checking for '$entry_text' or '$alt_text'");
3336 # Check if we already have this object
3338 for ($j = 0; $j <= $#hierarchy; $j++) {
3339 if (($hierarchy[$j] eq $entry_text) or ($hierarchy[$j] eq $alt_text)) {
3345 # We have a new entry, find insert position in alphabetical order
3347 for ($j = $last_index; $j <= $#hierarchy; $j++) {
3348 if ($hierarchy[$j] !~ m/^${indent}/) {
3352 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3353 my $stripped_text = $hierarchy[$j];
3354 if ($entry_text !~ m/<link linkend/) {
3355 $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3356 $stripped_text =~ s%</link>%%;
3358 if ($entry_text lt $stripped_text) {
3367 $last_index = 1 + $#hierarchy;
3369 splice @hierarchy, $last_index, 0, ($entry_text);
3372 # Already have this one, make sure we use the not linked version
3373 if ($entry_text !~ m/<link linkend=/) {
3374 $hierarchy[$j] = $entry_text;
3376 # Remember index as base insert point
3377 $last_index = $index + 1;
3381 # Output the children, indented and with links.
3382 for ($i = 0; $i <= $#children; $i++) {
3383 my $id = &CreateValidSGMLID ($children[$i]);
3384 my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3385 splice @hierarchy, $last_index, 0, ($indented_text);
3392 #############################################################################
3393 # Function : GetInterfaces
3394 # Description : Returns the DocBook output describing the interfaces
3395 # implemented by a class. It uses the global %Interfaces hash.
3396 # Arguments : $object - the GtkObject subclass.
3397 #############################################################################
3404 # Find object in the objects array.
3405 if (exists($Interfaces{$object})) {
3406 my @ifaces = split(' ', $Interfaces{$object});
3411 for ($i = 0; $i <= $#ifaces; $i++) {
3412 my $id = &CreateValidSGMLID ($ifaces[$i]);
3413 $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3414 if ($i < $#ifaces - 1) {
3417 elsif ($i < $#ifaces) {
3432 #############################################################################
3433 # Function : GetImplementations
3434 # Description : Returns the DocBook output describing the implementations
3435 # of an interface. It uses the global %Interfaces hash.
3436 # Arguments : $object - the GtkObject subclass.
3437 #############################################################################
3439 sub GetImplementations {
3444 foreach my $key (keys %Interfaces) {
3445 if ($Interfaces{$key} =~ /\b$object\b/) {
3446 push (@impls, $key);
3450 @impls = sort @impls;
3453 $object is implemented by
3455 for ($i = 0; $i <= $#impls; $i++) {
3456 my $id = &CreateValidSGMLID ($impls[$i]);
3457 $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3458 if ($i < $#impls - 1) {
3461 elsif ($i < $#impls) {
3476 #############################################################################
3477 # Function : GetPrerequisites
3478 # Description : Returns the DocBook output describing the prerequisites
3479 # of an interface. It uses the global %Prerequisites hash.
3480 # Arguments : $iface - the interface.
3481 #############################################################################
3483 sub GetPrerequisites {
3488 if (exists($Prerequisites{$iface})) {
3493 my @prereqs = split(' ', $Prerequisites{$iface});
3494 for ($i = 0; $i <= $#prereqs; $i++) {
3495 my $id = &CreateValidSGMLID ($prereqs[$i]);
3496 $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3497 if ($i < $#prereqs - 1) {
3500 elsif ($i < $#prereqs) {
3514 #############################################################################
3515 # Function : GetDerived
3516 # Description : Returns the DocBook output describing the derived interfaces
3517 # of an interface. It uses the global %Prerequisites hash.
3518 # Arguments : $iface - the interface.
3519 #############################################################################
3527 foreach my $key (keys %Prerequisites) {
3528 if ($Prerequisites{$key} =~ /\b$iface\b/) {
3529 push (@derived, $key);
3532 if ($#derived >= 0) {
3533 @derived = sort @derived;
3536 $iface is required by
3538 for ($i = 0; $i <= $#derived; $i++) {
3539 my $id = &CreateValidSGMLID ($derived[$i]);
3540 $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3541 if ($i < $#derived - 1) {
3544 elsif ($i < $#derived) {
3559 #############################################################################
3560 # Function : GetSignals
3561 # Description : Returns the synopsis and detailed description DocBook output
3562 # for the signal handlers of a given GtkObject subclass.
3563 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3564 #############################################################################
3572 for ($i = 0; $i <= $#SignalObjects; $i++) {
3573 if ($SignalObjects[$i] eq $object) {
3574 @TRACE@("Found signal: $SignalNames[$i]\n");
3575 my $name = $SignalNames[$i];
3576 my $symbol = "${object}::${name}";
3577 my $id = &CreateValidSGMLID ("$object-$name");
3579 $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3580 $desc .= MakeIndexterms($symbol, $id);
3582 $desc .= OutputSymbolExtraLinks($symbol);
3584 $desc .= "<programlisting language=\"C\">";
3586 $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3587 my $type_modifier = defined($1) ? $1 : "";
3590 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3592 my $ret_type_output = "$type_modifier$xref$pointer";
3593 my $callback_name = "user_function";
3594 $desc .= "${ret_type_output}\n${callback_name} (";
3596 my $indentation = ' ' x (length($callback_name) + 2);
3597 my $pad = $indentation;
3599 my $sourceparams = $SourceSymbolParams{$symbol};
3600 my @params = split ("\n", $SignalPrototypes[$i]);
3603 my $type_len = length("gpointer");
3604 my $name_len = length("user_data");
3605 # do two passes, the first one is to calculate padding
3606 for ($l = 0; $l < 2; $l++) {
3607 for ($j = 0; $j <= $#params; $j++) {
3609 # allow alphanumerics, '_', '[' & ']' in param names
3610 if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3613 if (defined($sourceparams)) {
3614 $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3619 if (!defined($param_name)) {
3620 $param_name = "arg$j";
3623 if (length($type) + length($pointer) > $type_len) {
3624 $type_len = length($type) + length($pointer);
3626 if (length($param_name) > $name_len) {
3627 $name_len = length($param_name);
3631 $xref = &MakeXRef ($type, &tagify($type, "type"));
3632 $pad = ' ' x ($type_len - length($type) - length($pointer));
3633 $desc .= "$xref$pad $pointer${param_name},\n";
3634 $desc .= $indentation;
3637 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3638 "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3642 $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3643 $pad = ' ' x ($type_len - length("gpointer"));
3644 $desc .= "$xref$pad user_data)";
3645 $desc .= "</programlisting>\n";
3647 my $flags = $SignalFlags[$i];
3648 my $flags_string = "";
3650 if (defined ($flags)) {
3651 if ($flags =~ m/f/) {
3652 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3654 elsif ($flags =~ m/l/) {
3655 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3657 elsif ($flags =~ m/c/) {
3658 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3659 $flags_string = "Cleanup";
3661 if ($flags =~ m/r/) {
3662 if ($flags_string) { $flags_string .= " / "; }
3663 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3665 if ($flags =~ m/d/) {
3666 if ($flags_string) { $flags_string .= " / "; }
3667 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3669 if ($flags =~ m/a/) {
3670 if ($flags_string) { $flags_string .= " / "; }
3671 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3673 if ($flags =~ m/h/) {
3674 if ($flags_string) { $flags_string .= " / "; }
3675 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3679 $synop .= "<row><entry role=\"signal_type\">${ret_type_output}</entry><entry role=\"signal_name\"><link linkend=\"$id\">${name}</link></entry><entry role=\"signal_flags\">${flags_string}</entry></row>\n";
3681 my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3683 $AllSymbols{$symbol} = 1;
3684 if (defined ($SymbolDocs{$symbol})) {
3685 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3687 $desc .= $symbol_docs;
3689 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3690 $AllDocumentedSymbols{$symbol} = 1;
3693 if (defined ($SymbolAnnotations{$symbol})) {
3694 my $param_desc = $SymbolAnnotations{$symbol};
3695 my $param_annotations = "";
3696 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3697 if ($param_annotations ne "") {
3698 $desc .= "\n<para>$param_annotations</para>";
3701 $desc .= &MakeDeprecationNote($symbol);
3703 $desc .= $parameters;
3704 if ($flags_string) {
3705 $desc .= "<para>Flags: $flags_string</para>\n";
3707 $desc .= OutputSymbolTraits ($symbol);
3708 $desc .= "</refsect2>";
3711 return ($synop, $desc);
3715 #############################################################################
3716 # Function : GetArgs
3717 # Description : Returns the synopsis and detailed description DocBook output
3718 # for the Args of a given GtkObject subclass.
3719 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3720 #############################################################################
3726 my $child_synop = "";
3727 my $child_desc = "";
3728 my $style_synop = "";
3729 my $style_desc = "";
3732 for ($i = 0; $i <= $#ArgObjects; $i++) {
3733 if ($ArgObjects[$i] eq $object) {
3734 @TRACE@("Found arg: $ArgNames[$i]\n");
3735 my $name = $ArgNames[$i];
3736 my $flags = $ArgFlags[$i];
3737 my $flags_string = "";
3741 if ($flags =~ m/c/) {
3742 $kind = "child property";
3745 elsif ($flags =~ m/s/) {
3746 $kind = "style property";
3753 # Remember only one colon so we don't clash with signals.
3754 my $symbol = "${object}:${name}";
3755 # use two dashes and ev. an extra separator here for the same reason.
3756 my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3758 my $type = $ArgTypes[$i];
3760 my $range = $ArgRanges[$i];
3761 my $range_output = CreateValidSGML ($range);
3762 my $default = $ArgDefaults[$i];
3763 my $default_output = CreateValidSGML ($default);
3765 if ($type eq "GtkString") {
3766 $type = "char *";
3768 if ($type eq "GtkSignal") {
3769 $type = "GtkSignalFunc, gpointer";
3770 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3771 . &MakeXRef ("gpointer");
3772 } elsif ($type =~ m/^(\w+)\*$/) {
3773 $type_output = &MakeXRef ($1, &tagify($1, "type")) . " *";
3775 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3778 if ($flags =~ m/r/) {
3779 $flags_string = "Read";
3781 if ($flags =~ m/w/) {
3782 if ($flags_string) { $flags_string .= " / "; }
3783 $flags_string .= "Write";
3785 if ($flags =~ m/x/) {
3786 if ($flags_string) { $flags_string .= " / "; }
3787 $flags_string .= "Construct";
3789 if ($flags =~ m/X/) {
3790 if ($flags_string) { $flags_string .= " / "; }
3791 $flags_string .= "Construct Only";
3794 $AllSymbols{$symbol} = 1;
3796 if (defined($SymbolDocs{$symbol}) &&
3797 !IsEmptyDoc($SymbolDocs{$symbol})) {
3798 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3799 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3800 $AllDocumentedSymbols{$symbol} = 1;
3803 if ($ArgBlurbs[$i] ne "") {
3804 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3805 $AllDocumentedSymbols{$symbol} = 1;
3807 # FIXME: print a warning?
3808 @TRACE@(".. no description\n");
3812 my $pad1 = " " x (24 - length ($name));
3814 my $arg_synop = "<row><entry role=\"property_type\">$type_output</entry><entry role=\"property_name\"><link linkend=\"$id\">$name</link></entry><entry role=\"property_flags\">$flags_string</entry></row>\n";
3815 my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3816 $arg_desc .= MakeIndexterms($symbol, $id);
3818 $arg_desc .= OutputSymbolExtraLinks($symbol);
3820 $arg_desc .= "<programlisting> “$name”$pad1 $type_output</programlisting>\n";
3821 $arg_desc .= $blurb;
3822 if (defined ($SymbolAnnotations{$symbol})) {
3823 my $param_desc = $SymbolAnnotations{$symbol};
3824 my $param_annotations = "";
3825 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3826 if ($param_annotations ne "") {
3827 $arg_desc .= "\n<para>$param_annotations</para>";
3830 $arg_desc .= &MakeDeprecationNote($symbol);
3832 if ($flags_string) {
3833 $arg_desc .= "<para>Flags: $flags_string</para>\n";
3836 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3838 if ($default ne "") {
3839 $arg_desc .= "<para>Default value: $default_output</para>\n";
3841 $arg_desc .= OutputSymbolTraits ($symbol);
3842 $arg_desc .= "</refsect2>\n";
3844 if ($flags =~ m/c/) {
3845 $child_synop .= $arg_synop;
3846 $child_desc .= $arg_desc;
3848 elsif ($flags =~ m/s/) {
3849 $style_synop .= $arg_synop;
3850 $style_desc .= $arg_desc;
3853 $synop .= $arg_synop;
3858 return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3862 #############################################################################
3863 # Function : ReadSourceDocumentation
3864 # Description : This reads in the documentation embedded in comment blocks
3865 # in the source code (for Gnome).
3867 # Parameter descriptions override any in the template files.
3868 # Function descriptions are placed before any description from
3869 # the template files.
3871 # It recursively descends the source directory looking for .c
3872 # files and scans them looking for specially-formatted comment
3875 # Arguments : $source_dir - the directory to scan.
3876 #############m###############################################################
3878 sub ReadSourceDocumentation {
3879 my ($source_dir) = @_;
3880 my ($file, $dir, @suffix_list, $suffix);
3882 # prepend entries from @SOURCE_DIR
3883 for my $dir (@SOURCE_DIRS) {
3884 # Check if the filename is in the ignore list.
3885 if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3886 @TRACE@("Skipping source directory: $source_dir");
3889 @TRACE@("No match for: ".($1 || $source_dir));
3893 @TRACE@("Scanning source directory: $source_dir");
3895 # This array holds any subdirectories found.
3898 @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3900 opendir (SRCDIR, $source_dir)
3901 || die "Can't open source directory $source_dir: $!";
3903 foreach $file (readdir (SRCDIR)) {
3904 if ($file =~ /^\./) {
3906 } elsif (-d "$source_dir/$file") {
3907 push (@subdirs, $file);
3908 } elsif (@suffix_list) {
3909 foreach $suffix (@suffix_list) {
3910 if ($file =~ m/\.\Q${suffix}\E$/) {
3911 &ScanSourceFile ("$source_dir/$file");
3914 } elsif ($file =~ m/\.[ch]$/) {
3915 &ScanSourceFile ("$source_dir/$file");
3920 # Now recursively scan the subdirectories.
3921 foreach $dir (@subdirs) {
3922 &ReadSourceDocumentation ("$source_dir/$dir");
3927 #############################################################################
3928 # Function : ScanSourceFile
3929 # Description : Scans one source file looking for specially-formatted comment
3930 # blocks. Later &MergeSourceDocumentation is used to merge any
3931 # documentation found with the documentation already read in
3932 # from the template files.
3934 # Arguments : $file - the file to scan.
3935 #############################################################################
3937 sub ScanSourceFile {
3941 # prepend entries from @SOURCE_DIR
3942 for my $dir (@SOURCE_DIRS) {
3943 # Check if the filename is in the ignore list.
3944 if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3945 @TRACE@("Skipping source file: $file");
3950 if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3953 &LogWarning ($file, 1, "Can't find basename for this filename.");
3957 # Check if the basename is in the list of files to ignore.
3958 if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3959 @TRACE@("Skipping source file: $file");
3963 @TRACE@("Scanning source file: $file");
3965 open (SRCFILE, $file)
3966 || die "Can't open $file: $!";
3967 my $in_comment_block = 0;
3970 my ($description, $return_desc);
3971 my ($since_desc, $stability_desc, $deprecated_desc);
3975 # Look for the start of a comment block.
3976 if (!$in_comment_block) {
3977 if (m%^\s*/\*.*\*/%) {
3978 #one-line comment - not gtkdoc
3979 } elsif (m%^\s*/\*\*\s%) {
3980 @TRACE@("Found comment block start\n");
3982 $in_comment_block = 1;
3984 # Reset all the symbol data.
3990 $deprecated_desc = "";
3991 $stability_desc = "";
3992 $current_param = -1;
3998 # We're in a comment block. Check if we've found the end of it.
4001 # maybe its not even meant to be a gtk-doc comment?
4002 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
4004 # Add the return value description onto the end of the params.
4006 # TODO(ensonic): check for duplicated Return docs
4007 # &LogWarning ($file, $., "Multiple Returns for $symbol.");
4008 push (@params, "Returns");
4009 push (@params, $return_desc);
4011 # Convert special characters
4012 $description = &ConvertSGMLChars ($symbol, $description);
4014 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4015 $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
4018 # Handle Section docs
4019 if ($symbol =~ m/SECTION:\s*(.*)/) {
4023 if (scalar %KnownSymbols) {
4024 if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
4025 &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
4029 @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
4030 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4031 @TRACE@(" '".$params[$k]."'\n");
4032 $params[$k] = "\L$params[$k]";
4034 if ($params[$k] eq "short_description") {
4035 $key = "$TMPL_DIR/$real_symbol:Short_Description";
4036 } elsif ($params[$k] eq "see_also") {
4037 $key = "$TMPL_DIR/$real_symbol:See_Also";
4038 } elsif ($params[$k] eq "title") {
4039 $key = "$TMPL_DIR/$real_symbol:Title";
4040 } elsif ($params[$k] eq "stability") {
4041 $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4042 } elsif ($params[$k] eq "section_id") {
4043 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4044 } elsif ($params[$k] eq "include") {
4045 $key = "$TMPL_DIR/$real_symbol:Include";
4046 } elsif ($params[$k] eq "image") {
4047 $key = "$TMPL_DIR/$real_symbol:Image";
4049 if (defined($key)) {
4050 $SourceSymbolDocs{$key}=$params[$k+1];
4051 $SourceSymbolSourceFile{$key} = $file;
4052 $SourceSymbolSourceLine{$key} = $.;
4055 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4056 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4057 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4058 #$SourceSymbolTypes{$symbol} = "SECTION";
4060 @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4061 $SourceSymbolDocs{$symbol} = $description;
4062 $SourceSymbolParams{$symbol} = [ @params ];
4063 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4064 #if (defined $DeclarationTypes{$symbol}) {
4065 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4067 $SourceSymbolSourceFile{$symbol} = $file;
4068 $SourceSymbolSourceLine{$symbol} = $.;
4072 ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4073 $since_desc =~ s/^\s+//;
4074 $since_desc =~ s/\s+$//;
4075 @TRACE@("Since($symbol) : [$since_desc]\n");
4076 $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4077 if(scalar @extra_lines) {
4078 &LogWarning ($file, $., "multi-line since docs found");
4082 if ($stability_desc) {
4083 $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4084 $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4087 if ($deprecated_desc) {
4088 if (!exists $Deprecated{$symbol}) {
4089 # don't warn for signals and properties
4090 #if ($symbol !~ m/::?(.*)/) {
4091 if (defined $DeclarationTypes{$symbol}) {
4092 &LogWarning ($file, $.,
4093 "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4094 " (See the --deprecated-guards option for gtkdoc-scan.)");
4097 $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4101 $in_comment_block = 0;
4105 # Get rid of ' * ' at start of every line in the comment block.
4107 # But make sure we don't get rid of the newline at the end.
4111 @TRACE@("scanning :$_");
4113 # If we haven't found the symbol name yet, look for it.
4115 if (m%^\s*(SECTION:\s*\S+)%) {
4117 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4118 } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._() ]+?\)\s*)*$%) {
4120 my $annotation = $2;
4121 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4122 if (defined($annotation)) {
4124 if ($annotation ne "") {
4125 $SymbolAnnotations{$symbol} = $annotation;
4126 @TRACE@("remaining text for $symbol: '$annotation'\n");
4133 if ($in_part eq "description") {
4134 # Get rid of 'Description:'
4135 s%^\s*Description:%%;
4138 if (m%^\s*(returns|return\s+value):%i) {
4139 # we're in param section and have not seen the blank line
4140 if($in_part ne "") {
4142 $in_part = "return";
4145 } elsif (m%^\s*since:%i) {
4146 # we're in param section and have not seen the blank line
4147 if($in_part ne "param") {
4152 } elsif (m%^\s*deprecated:%i) {
4153 # we're in param section and have not seen the blank line
4154 if($in_part ne "param") {
4155 $deprecated_desc = $';
4156 $in_part = "deprecated";
4159 } elsif (m%^\s*stability:%i) {
4160 $stability_desc = $';
4161 $in_part = "stability";
4165 if ($in_part eq "description") {
4168 } elsif ($in_part eq "return") {
4171 } elsif ($in_part eq "since") {
4174 } elsif ($in_part eq "stability") {
4175 $stability_desc .= $_;
4177 } elsif ($in_part eq "deprecated") {
4178 $deprecated_desc .= $_;
4182 # We must be in the parameters. Check for the empty line below them.
4184 $in_part = "description";
4188 # Look for a parameter name.
4189 if (m%^\s*@(\S+)\s*:\s*%) {
4190 my $param_name = $1;
4191 my $param_desc = $';
4193 @TRACE@("Found parameter: $param_name\n");
4194 # Allow varargs variations
4195 if ($param_name =~ m/^\.\.\.$/) {
4196 $param_name = "...";
4198 @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4200 push (@params, $param_name);
4201 push (@params, $param_desc);
4202 $current_param += $PARAM_FIELD_COUNT;
4205 } elsif ($in_part eq "") {
4206 @TRACE@("continuation for $symbol annotation '$_'");
4207 my $annotation = $_;
4208 $annotation =~ s/^\s+|\s+$//g ;
4209 $SymbolAnnotations{$symbol} .= $annotation;
4213 # We must be in the middle of a parameter description, so add it on
4214 # to the last element in @params.
4215 if ($current_param == -1) {
4216 &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
4218 $params[$#params] .= $_;
4224 #############################################################################
4225 # Function : OutputMissingDocumentation
4226 # Description : Outputs report of documentation coverage to a file
4229 #############################################################################
4231 sub OutputMissingDocumentation {
4232 my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4233 my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4235 my $n_documented = 0;
4236 my $n_incomplete = 0;
4242 my $buffer_deprecated = "";
4243 my $buffer_descriptions = "";
4245 open(UNDOCUMENTED, ">$new_undocumented_file")
4246 || die "Can't create $new_undocumented_file";
4248 foreach $symbol (sort (keys (%AllSymbols))) {
4249 # FIXME: should we print LogWarnings for undocumented stuff?
4251 #my $ssfile = &GetSymbolSourceFile($symbol);
4252 #my $ssline = &GetSymbolSourceLine($symbol);
4253 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4255 if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4257 if (exists ($AllDocumentedSymbols{$symbol})) {
4259 if (exists ($AllIncompleteSymbols{$symbol})) {
4261 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4262 #$buffer .= "\t0: ".$location;
4264 } elsif (exists $Deprecated{$symbol}) {
4265 if (exists ($AllIncompleteSymbols{$symbol})) {
4267 $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4268 #$buffer .= "\t1a: ".$location;
4270 $buffer_deprecated .= $symbol . "\n";
4271 #$buffer .= "\t1b: ".$location;
4274 if (exists ($AllIncompleteSymbols{$symbol})) {
4276 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4277 #$buffer .= "\t2a: ".$location;
4279 $buffer .= $symbol . "\n";
4280 #$buffer .= "\t2b: ".$location;
4283 } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4285 if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4286 || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4289 # cut off the leading namespace ($TMPL_DIR)
4290 $symbol =~ m/^.*\/(.*)$/;
4291 $buffer_descriptions .= $1 . "\n";
4299 $percent = ($n_documented / $total) * 100.0;
4302 printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4303 print UNDOCUMENTED "$n_documented symbols documented.\n";
4304 print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4305 print UNDOCUMENTED ($total - $n_documented) . " not documented.\n";
4307 if ($buffer_deprecated ne "") {
4308 $buffer .= "\n" . $buffer_deprecated;
4310 if ($buffer_descriptions ne "") {
4311 $buffer .= "\n" . $buffer_descriptions;
4313 if ($buffer ne "") {
4314 print UNDOCUMENTED "\n\n$buffer";
4316 close (UNDOCUMENTED);
4318 return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4320 printf "%.0f%% symbol docs coverage", $percent;
4321 print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4322 print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4326 #############################################################################
4327 # Function : OutputUndeclaredSymbols
4328 # Description : Outputs symbols that are listed in the section file, but not
4329 # declaration is found in the sources
4332 #############################################################################
4334 sub OutputUndeclaredSymbols {
4335 my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4336 my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4338 open(UNDECLARED, ">$new_undeclared_file")
4339 || die "Can't create $new_undeclared_file";
4341 if (%UndeclaredSymbols) {
4342 print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4343 print UNDECLARED "\n";
4344 print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4348 return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4351 #############################################################################
4352 # Function : OutputUnusedSymbols
4353 # Description : Outputs symbols that are documented in comments, but not
4354 # declared in the sources
4357 #############################################################################
4359 sub OutputUnusedSymbols {
4361 my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4362 my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4364 open (UNUSED, ">$new_unused_file")
4365 || die "Can't open $new_unused_file";
4367 foreach $symbol (sort keys (%Declarations)) {
4368 if (!defined ($DeclarationOutput{$symbol})) {
4369 print (UNUSED "$symbol\n");
4373 foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4374 print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4378 if ($num_unused != 0) {
4379 &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4380 "They should be added to $MODULE-sections.txt in the appropriate place.");
4383 return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4387 #############################################################################
4388 # Function : OutputAllSymbols
4389 # Description : Outputs list of all symbols to a file
4392 #############################################################################
4394 sub OutputAllSymbols {
4395 my $n_documented = 0;
4401 open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4402 || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4404 foreach $symbol (sort (keys (%AllSymbols))) {
4405 print SYMBOLS $symbol . "\n";
4411 #############################################################################
4412 # Function : OutputSymbolsWithoutSince
4413 # Description : Outputs list of all symbols without a since tag to a file
4416 #############################################################################
4418 sub OutputSymbolsWithoutSince {
4419 my $n_documented = 0;
4425 open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4426 || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4428 foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4429 if (!defined $Since{$symbol}) {
4430 print SYMBOLS $symbol . "\n";
4438 #############################################################################
4439 # Function : MergeSourceDocumentation
4440 # Description : This merges documentation read from a source file into the
4441 # documentation read in from a template file.
4443 # Parameter descriptions override any in the template files.
4444 # Function descriptions are placed before any description from
4445 # the template files.
4448 #############################################################################
4450 sub MergeSourceDocumentation {
4454 if (scalar %SymbolDocs) {
4455 @Symbols=keys (%SymbolDocs);
4456 @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4459 # filter scanned declarations, with what we suppress from -sections.txt
4461 foreach $symbol (keys (%Declarations)) {
4462 if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4466 # , add the rest from -sections.txt
4467 foreach $symbol (keys (%KnownSymbols)) {
4468 if ($KnownSymbols{$symbol} == 1) {
4472 # and add whats found in the source
4473 foreach $symbol (keys (%SourceSymbolDocs)) {
4476 @Symbols = keys (%tmp);
4477 @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4479 foreach $symbol (@Symbols) {
4480 $AllSymbols{$symbol} = 1;
4482 my $have_tmpl_docs = 0;
4484 ## see if the symbol is documented in template
4485 my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4486 my $check_tmpl_doc =$tmpl_doc;
4487 # remove all xml-tags and whitespaces
4488 $check_tmpl_doc =~ s/<.*?>//g;
4489 $check_tmpl_doc =~ s/\s//g;
4491 if ($check_tmpl_doc ne "") {
4492 $have_tmpl_docs = 1;
4494 # if the docs have just an empty para, don't merge that.
4495 $check_tmpl_doc = $tmpl_doc;
4496 $check_tmpl_doc =~ s/(\s|\n)//msg;
4497 if ($check_tmpl_doc eq "<para></para>") {
4502 if (exists ($SourceSymbolDocs{$symbol})) {
4503 my $type = $DeclarationTypes {$symbol};
4505 @TRACE@("merging [$symbol] from source\n");
4507 my $item = "Parameter";
4508 if (defined ($type)) {
4509 if ($type eq 'STRUCT') {
4511 } elsif ($type eq 'ENUM') {
4513 } elsif ($type eq 'UNION') {
4520 my $src_doc = $SourceSymbolDocs{$symbol};
4521 # remove leading and training whitespaces
4522 $src_doc =~ s/^\s+//;
4523 $src_doc =~ s/\s+$//;
4525 # Don't output warnings for overridden titles as titles are
4526 # automatically generated in the -sections.txt file, and thus they
4527 # are often overridden.
4528 if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4529 # check if content is different
4530 if ($tmpl_doc ne $src_doc) {
4531 #print "[$tmpl_doc] [$src_doc]\n";
4532 &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4533 "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4537 if ($src_doc ne "") {
4538 $AllDocumentedSymbols{$symbol} = 1;
4541 # Do not add <para> to nothing, it breaks missing docs checks.
4542 my $src_doc_para = "";
4543 if ($src_doc ne "") {
4544 $src_doc_para = $src_doc;
4547 if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4548 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4549 } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4550 # For the title/summary/see also section docs we don't want to
4551 # add any <para> tags.
4552 $SymbolDocs{$symbol} = "$src_doc"
4554 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4558 if ($symbol =~ m/.*::.*/) {
4559 # For signals we prefer the param names from the source docs,
4560 # since the ones from the templates are likely to contain the
4561 # artificial argn names which are generated by gtkdoc-scangobj.
4562 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4563 # FIXME: we need to check for empty docs here as well!
4565 # The templates contain the definitive parameter names and order,
4566 # so we will not change that. We only override the actual text.
4567 my $tmpl_params = $SymbolParams{$symbol};
4568 if (!defined ($tmpl_params)) {
4569 @TRACE@("No merge needed for $symbol\n");
4570 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4571 # FIXME: we still like to get the number of params and merge
4572 # 1) we would noticed that params have been removed/renamed
4573 # 2) we would catch undocumented params
4574 # params are not (yet) exported in -decl.txt so that we
4575 # could easily grab them :/
4577 my $params = $SourceSymbolParams{$symbol};
4579 @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4580 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4581 my $tmpl_param_name = $$tmpl_params[$j];
4583 # Try to find the param in the source comment documentation.
4586 @TRACE@(" try merge param $tmpl_param_name\n");
4587 for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4588 my $param_name = $$params[$k];
4589 my $param_desc = $$params[$k + 1];
4591 @TRACE@(" test param $param_name\n");
4592 # We accept changes in case, since the Gnome source
4593 # docs contain a lot of these.
4594 if ("\L$param_name" eq "\L$tmpl_param_name") {
4597 # Override the description.
4598 $$tmpl_params[$j + 1] = $param_desc;
4600 # Set the name to "" to mark it as used.
4606 # If it looks like the parameters are there, but not
4607 # in the right place, try to explain a bit better.
4608 if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4609 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4610 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4614 # Now we output a warning if parameters have been described which
4616 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4617 my $param_name = $$params[$j];
4619 # the template builder cannot detect if a macro returns
4621 if(($type eq "MACRO") && ($param_name eq "Returns")) {
4622 # FIXME: do we need to add it then to tmpl_params[] ?
4623 my $num=$#$tmpl_params;
4624 @TRACE@(" adding Returns: to macro docs for $symbol.\n");
4625 $$tmpl_params[$num+1]="Returns";
4626 $$tmpl_params[$num+2]=$$params[$j+1];
4629 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4630 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4636 if ($have_tmpl_docs) {
4637 $AllDocumentedSymbols{$symbol} = 1;
4638 @TRACE@("merging [$symbol] from template\n");
4641 @TRACE@("[$symbol] undocumented\n");
4645 # if this symbol is documented, check if docs are complete
4646 $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4647 # remove all xml-tags and whitespaces
4648 $check_tmpl_doc =~ s/<.*?>//g;
4649 $check_tmpl_doc =~ s/\s//g;
4650 if ($check_tmpl_doc ne "") {
4651 my $tmpl_params = $SymbolParams{$symbol};
4652 if (defined ($tmpl_params)) {
4653 my $type = $DeclarationTypes {$symbol};
4655 my $item = "Parameter";
4656 if (defined ($type)) {
4657 if ($type eq 'STRUCT') {
4659 } elsif ($type eq 'ENUM') {
4661 } elsif ($type eq 'UNION') {
4668 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4670 if ($#$tmpl_params > 0) {
4672 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4673 # Output a warning if the parameter is empty and
4674 # remember for stats.
4675 my $tmpl_param_name = $$tmpl_params[$j];
4676 my $tmpl_param_desc = $$tmpl_params[$j + 1];
4677 if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4678 if (exists ($AllIncompleteSymbols{$symbol})) {
4679 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4681 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4683 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4684 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4689 if ($#$tmpl_params == 0) {
4690 $AllIncompleteSymbols{$symbol}="<items>";
4691 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4692 "$item descriptions for $symbol are missing in source code comment block.");
4694 # $#$tmpl_params==-1 means we don't know about parameters
4695 # this unfortunately does not tell if there should be some
4700 @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4703 #############################################################################
4704 # Function : IsEmptyDoc
4705 # Description : Check if a doc-string is empty. Its also regarded as empty if
4706 # it only consist of whitespace or e.g. FIXME.
4707 # Arguments : the doc-string
4708 #############################################################################
4712 if ($doc =~ /^\s*$/) {
4716 if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4723 #############################################################################
4724 # Function : ConvertMarkDown
4725 # Description : Converts mark down syntax to the respective docbook.
4726 # http://de.wikipedia.org/wiki/Markdown
4727 # Inspired by the design of ParseDown
4728 # http://parsedown.org/
4729 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4730 # Arguments : the symbol name, the doc-string
4731 #############################################################################
4733 sub ConvertMarkDown {
4734 my ($symbol, $text) = @_;
4736 $text = &MarkDownParse ($text, $symbol);
4741 # SUPPORTED MARKDOWN
4742 # ==================
4751 # Setext-style Headers
4752 # --------------------
4760 # Ordered (unnested) Lists
4761 # ------------------------
4765 # 1. item 2 with loooong
4770 # Note: we require a blank line above the list items
4773 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4775 sub MarkDownParseBlocks {
4776 my ($linesref, $symbol, $context) = @_;
4779 my $md_block = { type => "" };
4781 OUTER: foreach $line (@$linesref) {
4782 my $first_char = substr ($line, 0, 1);
4783 my $deindented_line;
4785 @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
4787 if ($md_block->{"type"} eq "markup") {
4788 if (!$md_block->{"closed"}) {
4789 if (index ($line, $md_block->{"start"}) != -1) {
4790 $md_block->{"depth"}++;
4792 if (index ($line, $md_block->{"end"}) != -1) {
4793 if ($md_block->{"depth"} > 0) {
4794 $md_block->{"depth"}--;
4796 @TRACE@("closing tag '$line'");
4797 $md_block->{"closed"} = 1;
4798 # TODO(ensonic): reparse inner text with MarkDownParseLines?
4801 $md_block->{"text"} .= "\n" . $line;
4802 @TRACE@("add to markup");
4807 $deindented_line = $line;
4808 $deindented_line =~ s/^\s+//;
4810 if ($md_block->{"type"} eq "heading") {
4811 # a heading is ended by any level less than or equal
4812 if ($md_block->{"level"} == 1) {
4813 if ($line =~ /^={4,}[ \t]*$/) {
4814 my $text = pop @{$md_block->{"lines"}};
4815 $md_block->{"interrupted"} = 0;
4816 push @md_blocks, $md_block;
4818 $md_block = { type => "heading",
4823 } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4824 $md_block->{"interrupted"} = 0;
4825 push @md_blocks, $md_block;
4827 $md_block = { type => "heading",
4834 # push lines into the block until the end is reached
4835 push @{$md_block->{"lines"}}, $line;
4839 if ($line =~ /^[=]{4,}[ \t]*$/) {
4840 my $text = pop @{$md_block->{"lines"}};
4841 $md_block->{"interrupted"} = 0;
4842 push @md_blocks, $md_block;
4844 $md_block = { type => "heading",
4849 } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4850 my $text = pop @{$md_block->{"lines"}};
4851 $md_block->{"interrupted"} = 0;
4852 push @md_blocks, $md_block;
4854 $md_block = { type => "heading",
4859 } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4860 $md_block->{"interrupted"} = 0;
4861 push @md_blocks, $md_block;
4863 $md_block = { type => "heading",
4867 level => length($1) };
4870 # push lines into the block until the end is reached
4871 push @{$md_block->{"lines"}}, $line;
4875 } elsif ($md_block->{"type"} eq "code") {
4876 if ($line =~ /^[ \t]*\]\|(.*)/) {
4877 push @md_blocks, $md_block;
4878 $md_block = { type => "paragraph",
4882 push @{$md_block->{"lines"}}, $line;
4887 if ($deindented_line eq "") {
4888 $md_block->{"interrupted"} = 1;
4892 if ($md_block->{"type"} eq "quote") {
4893 if (!$md_block->{"interrupted"}) {
4894 $line =~ s/^[ ]*>[ ]?//;
4895 push @{$md_block->{"lines"}}, $line;
4898 } elsif ($md_block->{"type"} eq "li") {
4899 my $marker = $md_block->{"marker"};
4900 if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4901 my $indentation = $1;
4902 if ($md_block->{"indentation"} ne $indentation) {
4903 push @{$md_block->{"lines"}}, $line;
4906 my $ordered = $md_block->{"ordered"};
4907 $lines =~ s/^[ ]{0,4}//;
4908 $md_block->{"last"} = 0;
4909 push @md_blocks, $md_block;
4910 $md_block = { type => "li",
4911 ordered => $ordered,
4912 indentation => $indentation,
4916 lines => [ $lines ] };
4921 if ($md_block->{"interrupted"}) {
4922 if ($first_char eq " ") {
4923 push @{$md_block->{"lines"}}, "";
4924 $line =~ s/^[ ]{0,4}//;
4925 push @{$md_block->{"lines"}}, $line;
4926 $md_block->{"interrupted"} = 0;
4930 $line =~ s/^[ ]{0,4}//;
4931 push @{$md_block->{"lines"}}, $line;
4936 # indentation sensitive types
4937 @TRACE@("parsing '$line'");
4939 if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4941 push @md_blocks, $md_block;
4943 $md_block = { type => "heading",
4947 level => length($1) };
4950 } elsif ($line =~ /^={4,}[ \t]*$/) {
4951 # setext heading (====)
4953 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4954 push @md_blocks, $md_block;
4955 $md_block->{"type"} = "heading";
4956 $md_block->{"lines"} = [];
4957 $md_block->{"level"} = 1;
4961 } elsif ($line =~ /^-{4,}[ \t]*$/) {
4962 # setext heading (-----)
4964 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4965 push @md_blocks, $md_block;
4966 $md_block->{"type"} = "heading";
4967 $md_block->{"lines"} = [];
4968 $md_block->{"level"} = 2;
4972 } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4974 $md_block->{"interrupted"} = 1;
4975 push @md_blocks, $md_block;
4976 $md_block = { type => "code",
4982 # indentation insensitive types
4983 if ($line =~ /^[ ]*<!DOCTYPE/) {
4984 push @md_blocks, $md_block;
4986 $md_block = { type => "markup",
4987 text => $deindented_line,
4993 } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4994 # markup, including <?xml version="1.0"?>
4996 my $is_self_closing = defined($2);
4998 # skip link markdown
4999 # TODO(ensonic): consider adding more uri schemes (ftp, ...)
5000 if ($tag =~ /^https?/) {
5001 @TRACE@("skipping link '$tag'");
5003 # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
5004 # instead of creation a markdown block.
5005 my $scanning_for_end_of_text_level_tag = (
5006 $md_block->{"type"} eq "paragraph" &&
5007 defined($md_block->{"start"}) &&
5008 !$md_block->{"closed"});
5009 @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
5010 if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
5011 push @md_blocks, $md_block;
5013 if ($is_self_closing) {
5014 @TRACE@("self-closing docbook '$tag'");
5015 $md_block = { type => "self-closing tag",
5016 text => $deindented_line };
5017 $is_self_closing = 0;
5021 @TRACE@("new markup '$tag'");
5022 $md_block = { type => "markup",
5023 text => $deindented_line,
5024 start => "<" . $tag . ">",
5025 end => "</" . $tag . ">",
5028 if ($deindented_line =~ /<\/$tag>/) {
5029 $md_block->{"closed"} = 1;
5033 if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
5034 @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
5035 # TODO(ensonic): handle nesting
5036 if (!$scanning_for_end_of_text_level_tag) {
5037 if ($deindented_line !~ /<\/$tag>/) {
5038 @TRACE@("new text level markup '$tag'");
5039 $md_block->{"start"} = "<" . $tag . ">";
5040 $md_block->{"end"} = "</" . $tag . ">";
5041 $md_block->{"closed"} = 0;
5042 @TRACE@("scanning for end of '$tag'");
5045 if ($deindented_line =~ /$md_block->{"end"}/) {
5046 $md_block->{"closed"} = 1;
5047 @TRACE@("found end of '$tag'");
5053 } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
5055 push @md_blocks, $md_block;
5057 my $indentation = $1;
5058 $lines =~ s/^[ ]{0,4}//;
5059 $md_block = { type => "li",
5061 indentation => $indentation,
5065 lines => [ $lines ] };
5067 } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
5068 push @md_blocks, $md_block;
5069 $md_block = { type => "quote",
5075 if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5076 push @md_blocks, $md_block;
5078 my $indentation = $1;
5079 $lines =~ s/^[ ]{0,4}//;
5081 $md_block = { type => "li",
5083 indentation => $indentation,
5084 marker => "\\d+[.]",
5087 lines => [ $lines ] };
5093 if ($md_block->{"type"} eq "paragraph") {
5094 if ($md_block->{"interrupted"}) {
5095 push @md_blocks, $md_block;
5096 $md_block = { type => "paragraph",
5099 @TRACE@("new paragraph due to interrupted");
5101 $md_block->{"text"} .= "\n" . $line;
5102 @TRACE@("add to paragraph");
5105 push @md_blocks, $md_block;
5106 $md_block = { type => "paragraph",
5108 @TRACE@("new paragraph due to different block type");
5112 push @md_blocks, $md_block;
5119 sub MarkDownParseSpanElementsInner {
5120 my ($text, $markersref) = @_;
5122 my %markers = map { $_ => 1 } @$markersref;
5124 while ($text ne "") {
5125 my $closest_marker = "";
5126 my $closest_marker_index = 0;
5127 my $closest_marker_position = -1;
5128 my $text_marker = "";
5135 while ( ($marker, $use) = each %markers ) {
5136 my $marker_position;
5142 $marker_position = index ($text, $marker);
5144 if ($marker_position < 0) {
5145 $markers{$marker} = 0;
5149 if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5150 $closest_marker = $marker;
5151 $closest_marker_index = $i;
5152 $closest_marker_position = $marker_position;
5156 if ($closest_marker_position >= 0) {
5157 $text_marker = substr ($text, $closest_marker_position);
5160 if ($text_marker eq "") {
5166 $markup .= substr ($text, 0, $closest_marker_position);
5167 $text = substr ($text, $closest_marker_position);
5168 @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5170 if ($closest_marker eq "![" || $closest_marker eq "[") {
5173 if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5176 %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5179 $offset = length ($&);
5180 if ($element{"!"}) {
5184 $remaining_text = substr ($text, $offset);
5185 if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5190 $offset += length ($&);
5191 } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5192 $element{"ref"} = $1;
5193 $offset += length ($&);
5200 if ($element{"»"}) {
5201 $element{"»"} =~ s/&/&/g;
5202 $element{"»"} =~ s/</</g;
5204 if ($element{"!"}) {
5205 $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5207 if (defined ($element{"a"})) {
5208 $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5211 $markup .= "</inlinemediaobject>";
5212 } elsif ($element{"ref"}) {
5213 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5214 $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5216 if (defined ($element{"#"})) {
5217 # title attribute not supported
5220 $markup .= ">" . $element{"a"} . "</link>";
5222 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5223 $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5225 if (defined ($element{"#"})) {
5226 # title attribute not supported
5229 $markup .= ">" . $element{"a"} . "</ulink>";
5232 $markup .= $closest_marker;
5233 if ($closest_marker eq "![") {
5239 } elsif ($closest_marker eq "<") {
5240 if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5241 my $element_url = $1;
5242 $element_url =~ s/&/&/g;
5243 $element_url =~ s/</</g;
5245 $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5246 $offset = length ($&);
5247 } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5248 $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5249 $offset = length ($&);
5250 } elsif ($text =~ /^<[^>]+?>/) {
5252 $offset = length ($&);
5257 } elsif ($closest_marker eq "\\") {
5258 my $special_char = substr ($text, 1, 1);
5259 if ($MD_ESCAPABLE_CHARS{$special_char} ||
5260 $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5261 $markup .= $special_char;
5267 } elsif ($closest_marker eq "`") {
5268 if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5269 my $element_text = $2;
5270 $markup .= "<literal>" . $element_text . "</literal>";
5271 $offset = length ($&);
5276 } elsif ($closest_marker eq "@") {
5277 # Convert '@param()'
5278 # FIXME: we could make those also links ($symbol.$2), but that would be less
5279 # useful as the link target is a few lines up or down
5280 if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5281 $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5282 $offset = length ($&);
5283 } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5284 # Convert '@param', but not '\@param'.
5285 $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5286 $offset = length ($&);
5287 } elsif ($text =~ /^\\\@/) {
5289 $offset = length ($&);
5294 } elsif ($closest_marker eq "#") {
5295 if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5296 # handle #Object.func()
5297 $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5298 $offset = length ($&);
5299 } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5300 # Convert '#symbol', but not '\#symbol'.
5301 $markup .= $1 . &MakeHashXRef ($2, "type");
5302 $offset = length ($&);
5303 } elsif ($text =~ /^\\#/) {
5305 $offset = length ($&);
5310 } elsif ($closest_marker eq "%") {
5311 if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5312 # Convert '%constant', but not '\%constant'.
5313 # Also allow negative numbers, e.g. %-1.
5314 $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5315 $offset = length ($&);
5316 } elsif ($text =~ /^\\%/) {
5318 $offset = length ($&);
5326 $text = substr ($text, $offset);
5333 sub MarkDownParseSpanElements {
5335 my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5337 $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5339 # Convert 'function()' or 'macro()'.
5340 # if there is abc_*_def() we don't want to make a link to _def()
5341 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5342 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5347 sub ReplaceEntities {
5348 my ($text, $symbol) = @_;
5350 my @entities = ( [ "<", "<" ],
5359 [ "&", "&" ] ); # Do this last, or the others get messed up.
5362 # Expand entities in <programlisting> even inside CDATA since
5363 # we changed the definition of |[ to add CDATA
5364 for ($i = 0; $i <= $#entities; $i++) {
5365 $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5371 sub MarkDownOutputDocBook {
5372 my ($blocksref, $symbol, $context) = @_;
5375 my @blocks = @$blocksref;
5377 foreach $block (@blocks) {
5381 #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
5383 if ($block->{"type"} eq "paragraph") {
5384 $text = &MarkDownParseSpanElements ($block->{"text"});
5385 if ($context eq "li" && $output eq "") {
5386 if ($block->{"interrupted"}) {
5387 $output .= "\n<para>$text</para>\n";
5389 $output .= "<para>$text</para>";
5395 $output .= "<para>$text</para>\n";
5398 } elsif ($block->{"type"} eq "heading") {
5401 $title = &MarkDownParseSpanElements ($block->{"text"});
5403 if ($block->{"level"} == 1) {
5409 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5410 if (defined ($block->{"id"})) {
5411 $output .= "<$tag id=\"" . $block->{"id"} . "\">";
5413 $output .= "<$tag>";
5416 $output .= "<title>$title</title>$text</$tag>\n";
5417 } elsif ($block->{"type"} eq "li") {
5418 my $tag = "itemizedlist";
5420 if ($block->{"first"}) {
5421 if ($block->{"ordered"}) {
5422 $tag = "orderedlist";
5424 $output .= "<$tag>\n";
5427 if ($block->{"interrupted"}) {
5428 push @{$block->{"lines"}}, "";
5431 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5432 $output .= "<listitem>".$text."</listitem>\n";
5433 if ($block->{"last"}) {
5434 if ($block->{"ordered"}) {
5435 $tag = "orderedlist";
5437 $output .= "</$tag>\n";
5439 } elsif ($block->{"type"} eq "quote") {
5440 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5441 $output .= "<blockquote>\n$text</blockquote>\n";
5442 } elsif ($block->{"type"} eq "code") {
5443 my $tag = "programlisting";
5445 if ($block->{"language"}) {
5446 if ($block->{"language"} eq "plain") {
5447 $output .= "<informalexample><screen><![CDATA[\n";
5450 $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5453 $output .= "<informalexample><programlisting><![CDATA[\n";
5455 foreach (@{$block->{"lines"}}) {
5456 $output .= &ReplaceEntities ($_, $symbol) . "\n";
5458 $output .= "]]></$tag></informalexample>\n";
5459 } elsif ($block->{"type"} eq "markup") {
5460 $text = &ExpandAbbreviations($symbol, $block->{"text"});
5461 $output .= $text."\n";
5463 $output .= $block->{"text"}."\n";
5465 #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
5471 sub MarkDownParseLines {
5472 my ($linesref, $symbol, $context) = @_;
5474 my @lines = @$linesref;
5477 @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5478 $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5484 my ($text, $symbol) = @_;
5487 # take out some variability in line endings
5488 $text =~ s%\r\n%\n%g;
5492 @lines = split("\n", $text);
5493 $text = MarkDownParseLines(\@lines, $symbol, "");
5498 #############################################################################
5499 # LIBRARY FUNCTIONS - These functions are used in both gtkdoc-mkdb and
5500 # gtkdoc-mktmpl and should eventually be moved to a
5502 #############################################################################
5504 #############################################################################
5505 # Function : ReadDeclarationsFile
5506 # Description : This reads in a file containing the function/macro/enum etc.
5509 # Note that in some cases there are several declarations with
5510 # the same name, e.g. for conditional macros. In this case we
5511 # set a flag in the %DeclarationConditional hash so the
5512 # declaration is not shown in the docs.
5514 # If a macro and a function have the same name, e.g. for
5515 # gtk_object_ref, the function declaration takes precedence.
5517 # Some opaque structs are just declared with 'typedef struct
5518 # _name name;' in which case the declaration may be empty.
5519 # The structure may have been found later in the header, so
5520 # that overrides the empty declaration.
5522 # Arguments : $file - the declarations file to read
5523 # $override - if declarations in this file should override
5524 # any current declaration.
5525 #############################################################################
5527 sub ReadDeclarationsFile {
5528 my ($file, $override) = @_;
5530 if ($override == 0) {
5532 %DeclarationTypes = ();
5533 %DeclarationConditional = ();
5534 %DeclarationOutput = ();
5538 || die "Can't open $file: $!";
5539 my $declaration_type = "";
5540 my $declaration_name;
5542 my $is_deprecated = 0;
5544 if (!$declaration_type) {
5545 if (m/^<([^>]+)>/) {
5546 $declaration_type = $1;
5547 $declaration_name = "";
5548 @TRACE@("Found declaration: $declaration_type\n");
5552 if (m%^<NAME>(.*)</NAME>%) {
5553 $declaration_name = $1;
5554 } elsif (m%^<DEPRECATED/>%) {
5556 } elsif (m%^</$declaration_type>%) {
5557 @TRACE@("Found end of declaration: $declaration_name\n");
5558 # Check that the declaration has a name
5559 if ($declaration_name eq "") {
5560 &LogWarning ($file, $., "$declaration_type has no name.\n");
5563 # If the declaration is an empty typedef struct _XXX XXX
5564 # set the flag to indicate the struct has a typedef.
5565 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5566 && $declaration =~ m/^\s*$/) {
5567 @TRACE@("Struct has typedef: $declaration_name\n");
5568 $StructHasTypedef{$declaration_name} = 1;
5571 # Check if the symbol is already defined.
5572 if (defined ($Declarations{$declaration_name})
5573 && $override == 0) {
5574 # Function declarations take precedence.
5575 if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5577 } elsif ($declaration_type eq 'FUNCTION') {
5578 if ($is_deprecated) {
5579 $Deprecated{$declaration_name} = "";
5581 $Declarations{$declaration_name} = $declaration;
5582 $DeclarationTypes{$declaration_name} = $declaration_type;
5583 } elsif ($DeclarationTypes{$declaration_name}
5584 eq $declaration_type) {
5585 # If the existing declaration is empty, or is just a
5586 # forward declaration of a struct, override it.
5587 if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5588 if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5589 if ($is_deprecated) {
5590 $Deprecated{$declaration_name} = "";
5592 $Declarations{$declaration_name} = $declaration;
5593 } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5594 # Ignore an empty or forward declaration.
5596 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5599 # set flag in %DeclarationConditional hash for
5600 # multiply defined macros/typedefs.
5601 $DeclarationConditional{$declaration_name} = 1;
5604 &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5607 if ($is_deprecated) {
5608 $Deprecated{$declaration_name} = "";
5610 $Declarations{$declaration_name} = $declaration;
5611 $DeclarationTypes{$declaration_name} = $declaration_type;
5614 $declaration_type = "";
5625 #############################################################################
5626 # Function : ReadSignalsFile
5627 # Description : This reads in an existing file which contains information on
5628 # all GTK signals. It creates the arrays @SignalNames and
5629 # @SignalPrototypes containing info on the signals. The first
5630 # line of the SignalPrototype is the return type of the signal
5631 # handler. The remaining lines are the parameters passed to it.
5632 # The last parameter, "gpointer user_data" is always the same
5633 # so is not included.
5634 # Arguments : $file - the file containing the signal handler prototype
5636 #############################################################################
5638 sub ReadSignalsFile {
5646 my $signal_prototype;
5648 # Reset the signal info.
5649 @SignalObjects = ();
5651 @SignalReturns = ();
5653 @SignalPrototypes = ();
5658 if (!open (INPUT, $file)) {
5659 warn "Can't open $file - skipping signals\n";
5666 $signal_object = "";
5668 $signal_returns = "";
5669 $signal_prototype = "";
5672 if (m/^<NAME>(.*)<\/NAME>/) {
5674 if ($signal_name =~ m/^(.*)::(.*)$/) {
5675 $signal_object = $1;
5676 ($signal_name = $2) =~ s/_/-/g;
5677 @TRACE@("Found signal: $signal_name\n");
5679 &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5681 } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5682 $signal_returns = $1;
5683 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5685 } elsif (m%^</SIGNAL>%) {
5686 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5687 push (@SignalObjects, $signal_object);
5688 push (@SignalNames, $signal_name);
5689 push (@SignalReturns, $signal_returns);
5690 push (@SignalFlags, $signal_flags);
5691 push (@SignalPrototypes, $signal_prototype);
5694 $signal_prototype .= $_;
5702 #############################################################################
5703 # Function : ReadTemplateFile
5704 # Description : This reads in the manually-edited documentation file
5705 # corresponding to the file currently being created, so we can
5706 # insert the documentation at the appropriate places.
5707 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5708 # is a hash of arrays.
5709 # NOTE: This function is duplicated in gtkdoc-mktmpl (but
5710 # slightly different).
5711 # Arguments : $docsfile - the template file to read in.
5712 # $skip_unused_params - 1 if the unused parameters should be
5714 #############################################################################
5716 sub ReadTemplateFile {
5717 my ($docsfile, $skip_unused_params) = @_;
5719 my $template = "$docsfile.sgml";
5720 if (! -f $template) {
5721 @TRACE@("File doesn't exist: $template\n");
5725 # start with empty hashes, we merge the source comment for each file
5731 my $current_type = ""; # Type of symbol being read.
5732 my $current_symbol = ""; # Name of symbol being read.
5733 my $symbol_doc = ""; # Description of symbol being read.
5734 my @params; # Parameter names and descriptions of current
5735 # function/macro/function typedef.
5736 my $current_param = -1; # Index of parameter currently being read.
5737 # Note that the param array contains pairs
5738 # of param name & description.
5739 my $in_unused_params = 0; # True if we are reading in the unused params.
5740 my $in_deprecated = 0;
5742 my $in_stability = 0;
5744 open (DOCS, "$template")
5745 || die "Can't open $template: $!";
5747 @TRACE@("reading template $template");
5750 if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5753 if ($symbol eq "Title"
5754 || $symbol eq "Short_Description"
5755 || $symbol eq "Long_Description"
5756 || $symbol eq "See_Also"
5757 || $symbol eq "Stability_Level"
5758 || $symbol eq "Include"
5759 || $symbol eq "Image") {
5761 $symbol = $docsfile . ":" . $symbol;
5764 @TRACE@("Found symbol: $symbol\n");
5765 # Remember file and line for the symbol
5766 $SymbolSourceFile{$symbol} = $template;
5767 $SymbolSourceLine{$symbol} = $.;
5769 # Store previous symbol, but remove any trailing blank lines.
5770 if ($current_symbol ne "") {
5771 $symbol_doc =~ s/\s+$//;
5772 $SymbolTypes{$current_symbol} = $current_type;
5773 $SymbolDocs{$current_symbol} = $symbol_doc;
5775 # Check that the stability level is valid.
5776 if ($StabilityLevel{$current_symbol}) {
5777 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5780 if ($current_param >= 0) {
5781 $SymbolParams{$current_symbol} = [ @params ];
5783 # Delete any existing params in case we are overriding a
5784 # previously read template.
5785 delete $SymbolParams{$current_symbol};
5788 $current_type = $type;
5789 $current_symbol = $symbol;
5790 $current_param = -1;
5791 $in_unused_params = 0;
5798 } elsif (m/^<!-- # Unused Parameters # -->/) {
5799 @TRACE@("Found unused parameters\n");
5800 $in_unused_params = 1;
5803 } elsif ($in_unused_params && $skip_unused_params) {
5804 # When outputting the DocBook we skip unused parameters.
5805 @TRACE@("Skipping unused param: $_");
5809 # Check if param found. Need to handle "..." and "format...".
5810 if (s/^\@([\w\.]+):\040?//) {
5811 my $param_name = $1;
5812 my $param_desc = $_;
5813 # Allow variations of 'Returns'
5814 if ($param_name =~ m/^[Rr]eturns?$/) {
5815 $param_name = "Returns";
5817 # Allow varargs variations
5818 if ($param_name =~ m/^.*\.\.\.$/) {
5819 $param_name = "...";
5822 # strip trailing whitespaces and blank lines
5825 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5827 if ($param_name eq "Deprecated") {
5829 $Deprecated{$current_symbol} = $_;
5830 } elsif ($param_name eq "Since") {
5833 $Since{$current_symbol} = $_;
5834 } elsif ($param_name eq "Stability") {
5836 $StabilityLevel{$current_symbol} = $_;
5838 push (@params, $param_name);
5839 push (@params, $param_desc);
5840 $current_param += $PARAM_FIELD_COUNT;
5843 # strip trailing whitespaces and blank lines
5848 if ($in_deprecated) {
5849 $Deprecated{$current_symbol} .= $_;
5850 } elsif ($in_since) {
5851 &LogWarning ($template, $., "multi-line since docs found");
5852 #$Since{$current_symbol} .= $_;
5853 } elsif ($in_stability) {
5854 $StabilityLevel{$current_symbol} .= $_;
5855 } elsif ($current_param >= 0) {
5856 $params[$current_param] .= $_;
5865 # Remember to finish the current symbol doccs.
5866 if ($current_symbol ne "") {
5868 $symbol_doc =~ s/\s+$//;
5869 $SymbolTypes{$current_symbol} = $current_type;
5870 $SymbolDocs{$current_symbol} = $symbol_doc;
5872 # Check that the stability level is valid.
5873 if ($StabilityLevel{$current_symbol}) {
5874 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5877 if ($current_param >= 0) {
5878 $SymbolParams{$current_symbol} = [ @params ];
5880 # Delete any existing params in case we are overriding a
5881 # previously read template.
5882 delete $SymbolParams{$current_symbol};
5891 #############################################################################
5892 # Function : ReadObjectHierarchy
5893 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5894 # the GtkObject subclasses described in this module (and their
5896 # It places them in the @Objects array, and places their level
5897 # in the object hierarchy in the @ObjectLevels array, at the
5898 # same index. GtkObject, the root object, has a level of 1.
5900 # This also generates tree_index.sgml as it goes along.
5903 #############################################################################
5905 sub ReadObjectHierarchy {
5909 if (! -f $OBJECT_TREE_FILE) {
5912 if (!open (INPUT, $OBJECT_TREE_FILE)) {
5913 warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5917 # Only emit objects if they are supposed to be documented, or if
5918 # they have documented children. To implement this, we maintain a
5919 # stack of pending objects which will be emitted if a documented
5921 my @pending_objects = ();
5922 my @pending_levels = ();
5928 my $level = (length($`)) / 2 + 1;
5935 while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5936 my $pobject = pop(@pending_objects);
5937 my $plevel = pop(@pending_levels);
5940 push (@pending_objects, $object);
5941 push (@pending_levels, $level);
5943 if (exists($KnownSymbols{$object})) {
5944 while ($#pending_levels >= 0) {
5945 $object = shift @pending_objects;
5946 $level = shift @pending_levels;
5947 $xref = &MakeXRef ($object);
5949 push (@tree, ' ' x ($level * 4) . "$xref");
5950 push (@Objects, $object);
5951 push (@ObjectLevels, $level);
5952 $ObjectRoots{$object} = $root;
5956 # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5963 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
5964 my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
5965 my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
5967 open (OUTPUT, ">$new_tree_index")
5968 || die "Can't create $new_tree_index: $!";
5970 print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
5973 &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5978 #############################################################################
5979 # Function : ReadInterfaces
5980 # Description : This reads in the $MODULE.interfaces file.
5983 #############################################################################
5985 sub ReadInterfaces {
5988 if (! -f $INTERFACES_FILE) {
5991 if (!open (INPUT, $INTERFACES_FILE)) {
5992 warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5998 my ($object, @ifaces) = split;
5999 if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
6000 my @knownIfaces = ();
6002 # filter out private interfaces, but leave foreign interfaces
6003 foreach my $iface (@ifaces) {
6004 if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
6005 push (@knownIfaces, $iface);
6009 $Interfaces{$object} = join(' ', @knownIfaces);
6010 @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
6012 @TRACE@("skipping interfaces for unknown symbol: $object\n");
6018 #############################################################################
6019 # Function : ReadPrerequisites
6020 # Description : This reads in the $MODULE.prerequisites file.
6023 #############################################################################
6025 sub ReadPrerequisites {
6026 %Prerequisites = ();
6028 if (! -f $PREREQUISITES_FILE) {
6031 if (!open (INPUT, $PREREQUISITES_FILE)) {
6032 warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
6038 my ($iface, @prereqs) = split;
6039 if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
6040 my @knownPrereqs = ();
6042 # filter out private prerequisites, but leave foreign prerequisites
6043 foreach my $prereq (@prereqs) {
6044 if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
6045 push (@knownPrereqs, $prereq);
6049 $Prerequisites{$iface} = join(' ', @knownPrereqs);
6055 #############################################################################
6056 # Function : ReadArgsFile
6057 # Description : This reads in an existing file which contains information on
6058 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
6059 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
6061 # Arguments : $file - the file containing the arg information.
6062 #############################################################################
6077 # Reset the args info.
6090 if (!open (INPUT, $file)) {
6091 warn "Can't open $file - skipping args\n";
6108 if (m/^<NAME>(.*)<\/NAME>/) {
6110 if ($arg_name =~ m/^(.*)::(.*)$/) {
6112 ($arg_name = $2) =~ s/_/-/g;
6113 @TRACE@("Found arg: $arg_name\n");
6115 &LogWarning ($file, $., "Invalid argument name: $arg_name");
6117 } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6119 } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6121 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6123 } elsif (m/^<NICK>(.*)<\/NICK>/) {
6125 } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6127 if ($arg_blurb eq "(null)") {
6129 &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6131 } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6133 } elsif (m%^</ARG>%) {
6134 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6135 push (@ArgObjects, $arg_object);
6136 push (@ArgNames, $arg_name);
6137 push (@ArgTypes, $arg_type);
6138 push (@ArgRanges, $arg_range);
6139 push (@ArgFlags, $arg_flags);
6140 push (@ArgNicks, $arg_nick);
6141 push (@ArgBlurbs, $arg_blurb);
6142 push (@ArgDefaults, $arg_default);
6150 #############################################################################
6151 # Function : AddTreeLineArt
6152 # Description : Add unicode lineart to a pre-indented string array and returns
6153 # it as as multiline string.
6154 # Arguments : @tree - array of indented strings.
6155 #############################################################################
6157 sub AddTreeLineArt {
6158 my @tree = @{$_[0]};
6163 # iterate bottom up over the tree
6164 for ($i = $#tree; $i >= 0; $i--) {
6165 # count leading spaces
6166 $tree[$i] =~ /^([^<A-Za-z]*)/;
6167 $indent = length( $1 );
6168 # replace with ╰───, if place of ╰ is not space insert ├
6170 if (substr($tree[$i],$indent-4,1) eq " ") {
6171 substr($tree[$i],$indent-4,4) = "--- ";
6173 substr($tree[$i],$indent-4,4) = "+-- ";
6175 # go lines up while space and insert |
6176 for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6177 substr($tree[$j],$indent-4,1) = '|';
6182 my $res = join("\n", @tree);
6183 # unicode chars for: ╰──
6184 $res =~ s%---%<phrase role=\"lineart\">╰──</phrase>%g;
6185 # unicde chars for: ├──
6186 $res =~ s%\+--%<phrase role=\"lineart\">├──</phrase>%g;
6187 # unicode char for: │
6188 $res =~ s%\|%<phrase role=\"lineart\">│</phrase>%g;
6194 #############################################################################
6195 # Function : CheckIsObject
6196 # Description : Returns 1 if the given name is a GObject or a subclass.
6197 # It uses the global @Objects array.
6198 # Note that the @Objects array only contains classes in the
6199 # current module and their ancestors - not all GObject classes.
6200 # Arguments : $name - the name to check.
6201 #############################################################################
6205 my $root = $ObjectRoots{$name};
6206 # Let GBoxed pass as an object here to get -struct appended to the id
6207 # and prevent conflicts with sections.
6208 return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6212 #############################################################################
6213 # Function : MakeReturnField
6214 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6215 # Arguments : $str - the string to pad.
6216 #############################################################################
6218 sub MakeReturnField {
6221 return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6224 #############################################################################
6225 # Function : GetSymbolSourceFile
6226 # Description : Get the filename where the symbol docs where taken from.
6227 # Arguments : $symbol - the symbol name
6228 #############################################################################
6230 sub GetSymbolSourceFile {
6233 if (defined($SourceSymbolSourceFile{$symbol})) {
6234 return $SourceSymbolSourceFile{$symbol};
6235 } elsif (defined($SymbolSourceFile{$symbol})) {
6236 return $SymbolSourceFile{$symbol};
6242 #############################################################################
6243 # Function : GetSymbolSourceLine
6244 # Description : Get the file line where the symbol docs where taken from.
6245 # Arguments : $symbol - the symbol name
6246 #############################################################################
6248 sub GetSymbolSourceLine {
6251 if (defined($SourceSymbolSourceLine{$symbol})) {
6252 return $SourceSymbolSourceLine{$symbol};
6253 } elsif (defined($SymbolSourceLine{$symbol})) {
6254 return $SymbolSourceLine{$symbol};