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}) && $$hash{short} ne "") {
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' (derived from $short_symbol)\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);
2588 #############################################################################
2589 # Function : OutputProgramDBFile
2590 # Description : Outputs the final DocBook file for one program.
2591 # Arguments : $file - the name of the file.
2592 # $section_id - the id to use for the toplevel tag.
2593 #############################################################################
2595 sub OutputProgramDBFile {
2596 my ($program, $section_id) = @_;
2598 @TRACE@("Output program docbook for $program\n");
2600 my $short_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Short_Description"};
2601 if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2604 # Don't use ConvertMarkDown here for now since we don't want blocks
2605 $short_desc = &ExpandAbbreviations("$program", $short_desc);
2606 @TRACE@("Found short_desc: $short_desc");
2609 my $synopsis = $SourceSymbolDocs{"$TMPL_DIR/$program:Synopsis"};
2610 if (defined ($synopsis) && $synopsis !~ m/^\s*$/) {
2612 my @items = split(' ', $synopsis);
2613 for ($i = 0; $i <= $#items; $i++) {
2614 my $parameter = $items[$i];
2615 my $choice = "plain";
2618 # first parameter is the command name
2620 $synopsis = "<command>$parameter</command>\n";
2624 # square brackets indicate optional parameters, curly brackets
2625 # indicate required parameters ("plain" parameters are also
2626 # mandatory, but do not get extra decoration)
2627 if ($parameter =~ s/^\[(.+?)\]$/$1/) {
2629 } elsif ($parameter =~ s/^\{(.+?)\}$/$1/) {
2633 # parameters ending in "..." are repeatable
2634 if ($parameter =~ s/\.\.\.$//) {
2635 $rep = " rep=\"repeat\"";
2638 # italic parameters are replaceable parameters
2639 if ($parameter =~ s/\*(.+?)\*/$1/) {
2640 $parameter = "<replaceable>$parameter</replaceable>";
2643 $synopsis .= "<arg choice=\"$choice\"$rep>";
2644 $synopsis .= $parameter;
2645 $synopsis .= "</arg>\n";
2648 @TRACE@("Found synopsis: $synopsis");
2650 $synopsis = "<command>$program</command>";
2653 my $long_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Long_Description"};
2654 if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2657 $long_desc = &ConvertMarkDown("$program:Long_description", $long_desc);
2658 @TRACE@("Found long_desc: $long_desc");
2662 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$program:Options"})) {
2663 my @opts = @{ $SourceSymbolDocs{"$TMPL_DIR/$program:Options"} };
2666 $options = "<refsect1>\n<title>Options</title>\n<variablelist>\n";
2667 for ($k = 0; $k <= $#opts; $k += 2) {
2668 my $opt_desc = $opts[$k+1];
2672 $opt_desc =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
2674 $options .= "<varlistentry>\n<term>";
2675 @opt_names = split (',', $opts[$k]);
2676 for ($i = 0; $i <= $#opt_names; $i++) {
2677 my $prefix = ($i > 0) ? ", " : "";
2678 $opt_names[$i] =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
2680 $options .= "$prefix<option>$opt_names[$i]</option>\n";
2682 $options .= "</term>\n";
2683 $options .= "<listitem><para>$opt_desc</para></listitem>\n";
2684 $options .= "</varlistentry>\n";
2686 $options .= "</variablelist></refsect1>\n";
2689 my $exit_status = $SourceSymbolDocs{"$TMPL_DIR/$program:Returns"};
2690 if (defined ($exit_status) && $exit_status ne "") {
2691 $exit_status = &ConvertMarkDown("$program:Returns", $exit_status);
2692 $exit_status = "<refsect1 id=\"$section_id.exit-status\">\n<title>Exit Status</title>\n$exit_status\n</refsect1>\n";
2697 my $see_also = $SourceSymbolDocs{"$TMPL_DIR/$program:See_Also"};
2698 if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2701 $see_also = &ConvertMarkDown("$program:See_Also", $see_also);
2702 @TRACE@("Found see_also: $see_also");
2705 $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2708 my $old_db_file = "$DB_OUTPUT_DIR/$program.xml";
2709 my $new_db_file = "$DB_OUTPUT_DIR/$program.xml.new";
2711 open (OUTPUT, ">$new_db_file")
2712 || die "Can't create $new_db_file: $!";
2715 ${\( MakeDocHeader ("refentry") )}
2716 <refentry id="$section_id">
2718 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$program</refentrytitle>
2719 <manvolnum>1</manvolnum>
2720 <refmiscinfo>User Commands</refmiscinfo>
2723 <refname>$program</refname>
2724 <refpurpose>$short_desc</refpurpose>
2727 <cmdsynopsis>$synopsis</cmdsynopsis>
2729 <refsect1 id="$section_id.description" role="desc">
2730 <title role="desc.title">Description</title>
2733 $options$exit_status$see_also
2738 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2743 #############################################################################
2744 # Function : OutputExtraFile
2745 # Description : Copies an "extra" DocBook file into the output directory,
2746 # expanding abbreviations
2747 # Arguments : $file - the source file.
2748 #############################################################################
2749 sub OutputExtraFile {
2754 ($basename = $file) =~ s!^.*/!!;
2756 my $old_db_file = "$DB_OUTPUT_DIR/$basename";
2757 my $new_db_file = "$DB_OUTPUT_DIR/$basename.new";
2761 open(EXTRA_FILE, "<$file") || die "Can't open $file";
2765 $contents = <EXTRA_FILE>;
2768 open (OUTPUT, ">$new_db_file")
2769 || die "Can't create $new_db_file: $!";
2771 print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2774 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2776 #############################################################################
2777 # Function : OutputBook
2778 # Description : Outputs the entities that need to be included into the
2779 # main docbook file for the module.
2780 # Arguments : $book_top - the declarations of the entities, which are added
2781 # at the top of the main docbook file.
2782 # $book_bottom - the references to the entities, which are
2783 # added in the main docbook file at the desired position.
2784 #############################################################################
2787 my ($book_top, $book_bottom) = @_;
2789 my $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.top";
2790 my $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.top.new";
2792 open (OUTPUT, ">$new_file")
2793 || die "Can't create $new_file: $!";
2794 print OUTPUT $book_top;
2797 &UpdateFileIfChanged ($old_file, $new_file, 0);
2800 $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom";
2801 $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom.new";
2803 open (OUTPUT, ">$new_file")
2804 || die "Can't create $new_file: $!";
2805 print OUTPUT $book_bottom;
2808 &UpdateFileIfChanged ($old_file, $new_file, 0);
2811 # If the main docbook file hasn't been created yet, we create it here.
2812 # The user can tweak it later.
2813 if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2814 open (OUTPUT, ">$MAIN_SGML_FILE")
2815 || die "Can't create $MAIN_SGML_FILE: $!";
2818 ${\( MakeDocHeader ("book") )}
2821 <title>&package_name; Reference Manual</title>
2823 for &package_string;.
2824 The latest version of this documentation can be found on-line at
2825 <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2830 <title>[Insert title here]</title>
2834 if (-e $OBJECT_TREE_FILE) {
2836 <chapter id="object-tree">
2837 <title>Object Hierarchy</title>
2838 <xi:include href="xml/tree_index.sgml"/>
2843 <!-- enable this when you use gobject types
2844 <chapter id="object-tree">
2845 <title>Object Hierarchy</title>
2846 <xi:include href="xml/tree_index.sgml"/>
2852 <index id="api-index-full">
2853 <title>API Index</title>
2854 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2856 <index id="deprecated-api-index" role="deprecated">
2857 <title>Index of deprecated API</title>
2858 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2861 if (keys(%AnnotationsUsed)) {
2863 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2867 <!-- enable this when you use gobject introspection annotations
2868 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2881 #############################################################################
2882 # Function : CreateValidSGML
2883 # Description : This turns any chars which are used in SGML into entities,
2884 # e.g. '<' into '<'
2885 # Arguments : $text - the text to turn into proper SGML.
2886 #############################################################################
2888 sub CreateValidSGML {
2890 $text =~ s/&/&/g; # Do this first, or the others get messed up.
2891 $text =~ s/</</g;
2892 $text =~ s/>/>/g;
2893 # browers render single tabs inconsistently
2894 $text =~ s/([^\s])\t([^\s])/$1 $2/g;
2898 #############################################################################
2899 # Function : ConvertSGMLChars
2900 # Description : This is used for text in source code comment blocks, to turn
2901 # chars which are used in SGML into entities, e.g. '<' into
2902 # '<'. Depending on $INLINE_MARKUP_MODE, this is done
2903 # unconditionally or only if the character doesn't seem to be
2904 # part of an SGML construct (tag or entity reference).
2905 # Arguments : $text - the text to turn into proper SGML.
2906 #############################################################################
2908 sub ConvertSGMLChars {
2909 my ($symbol, $text) = @_;
2911 if ($INLINE_MARKUP_MODE) {
2912 # For the XML/SGML mode only convert to entities outside CDATA sections.
2913 return &ModifyXMLElements ($text, $symbol,
2914 "<!\\[CDATA\\[|<programlisting[^>]*>",
2915 \&ConvertSGMLCharsEndTag,
2916 \&ConvertSGMLCharsCallback);
2918 # For the simple non-sgml mode, convert to entities everywhere.
2920 # First, convert freestanding & to &
2921 $text =~ s/&(?![a-zA-Z#]+;)/&/g;
2922 $text =~ s/</</g;
2923 # Allow ">" at beginning of string for blockquote markdown
2924 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2931 sub ConvertSGMLCharsEndTag {
2932 if ($_[0] eq "<!\[CDATA\[") {
2935 return "</programlisting>";
2939 sub ConvertSGMLCharsCallback {
2940 my ($text, $symbol, $tag) = @_;
2942 if ($tag =~ m/^<programlisting/) {
2943 # We can handle <programlisting> specially here.
2944 return &ModifyXMLElements ($text, $symbol,
2946 \&ConvertSGMLCharsEndTag,
2947 \&ConvertSGMLCharsCallback2);
2948 } elsif ($tag eq "") {
2949 # If we're not in CDATA convert to entities.
2950 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2951 $text =~ s/<(?![a-zA-Z\/!])/</g;
2952 # Allow ">" at beginning of string for blockquote markdown
2953 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2955 # Handle "#include <xxxxx>"
2956 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2962 sub ConvertSGMLCharsCallback2 {
2963 my ($text, $symbol, $tag) = @_;
2965 # If we're not in CDATA convert to entities.
2966 # We could handle <programlisting> differently, though I'm not sure it helps.
2968 # replace only if its not a tag
2969 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2970 $text =~ s/<(?![a-zA-Z\/!])/</g;
2971 $text =~ s/(?<![a-zA-Z0-9"'\/-])>/>/g;
2973 # Handle "#include <xxxxx>"
2974 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2980 #############################################################################
2981 # Function : ExpandAnnotation
2982 # Description : This turns annotations into acronym tags.
2983 # Arguments : $symbol - the symbol being documented, for error messages.
2984 # $text - the text to expand.
2985 #############################################################################
2986 sub ExpandAnnotation {
2987 my ($symbol, $param_desc) = @_;
2988 my $param_annotations = "";
2990 # look for annotations at the start of the comment part
2991 # function level annotations don't end with a colon ':'
2992 if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
2997 @annotations = split(/\)\s*\(/,$1);
2998 @TRACE@("annotations for $symbol: '$1'\n");
2999 foreach $annotation (@annotations) {
3000 # need to search for the longest key-match in %AnnotationDefinition
3002 my $match_annotation="";
3004 foreach $annotationdef (keys %AnnotationDefinition) {
3005 if ($annotation =~ m/^$annotationdef/) {
3006 if (length($annotationdef)>$match_length) {
3007 $match_length=length($annotationdef);
3008 $match_annotation=$annotationdef;
3012 my $annotation_extra = "";
3013 if ($match_annotation ne "") {
3014 if ($annotation =~ m%$match_annotation\s+(.*)%) {
3015 $annotation_extra = " $1";
3017 $AnnotationsUsed{$match_annotation} = 1;
3018 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
3021 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3022 "unknown annotation \"$annotation\" in documentation for $symbol.");
3023 $param_annotations .= "[$annotation]";
3027 $param_desc =~ m/^(.*?)\.*\s*$/s;
3028 $param_desc = "$1. ";
3030 if ($param_annotations ne "") {
3031 $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
3033 return ($param_desc, $param_annotations);
3036 #############################################################################
3037 # Function : ExpandAbbreviations
3038 # Description : This turns the abbreviations function(), macro(), @param,
3039 # %constant, and #symbol into appropriate DocBook markup.
3040 # CDATA sections and <programlisting> parts are skipped.
3041 # Arguments : $symbol - the symbol being documented, for error messages.
3042 # $text - the text to expand.
3043 #############################################################################
3045 sub ExpandAbbreviations {
3046 my ($symbol, $text) = @_;
3048 # Note: This is a fallback and normally done in the markdown parser
3050 # Convert "|[" and "]|" into the start and end of program listing examples.
3051 # Support \[<!-- language="C" --> modifiers
3052 $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
3053 $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
3054 $text =~ s%\]\|%]]></programlisting></informalexample>%g;
3056 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
3058 return &ModifyXMLElements ($text, $symbol,
3059 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
3060 \&ExpandAbbreviationsEndTag,
3061 \&ExpandAbbreviationsCallback);
3065 # Returns the end tag (as a regexp) corresponding to the given start tag.
3066 sub ExpandAbbreviationsEndTag {
3067 my ($start_tag) = @_;
3069 if ($start_tag eq "<!\[CDATA\[") {
3071 } elsif ($start_tag eq "<!DOCTYPE") {
3073 } elsif ($start_tag =~ m/<(\w+)/) {
3078 # Called inside or outside each CDATA or <programlisting> section.
3079 sub ExpandAbbreviationsCallback {
3080 my ($text, $symbol, $tag) = @_;
3082 if ($tag =~ m/^<programlisting/) {
3083 # Handle any embedded CDATA sections.
3084 return &ModifyXMLElements ($text, $symbol,
3086 \&ExpandAbbreviationsEndTag,
3087 \&ExpandAbbreviationsCallback2);
3088 } elsif ($tag eq "") {
3089 # NOTE: this is a fallback. It is normally done by the Markdown parser.
3091 # We are outside any CDATA or <programlisting> sections, so we expand
3092 # any gtk-doc abbreviations.
3094 # Convert '@param()'
3095 # FIXME: we could make those also links ($symbol.$2), but that would be less
3096 # useful as the link target is a few lines up or down
3097 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
3099 # Convert 'function()' or 'macro()'.
3100 # if there is abc_*_def() we don't want to make a link to _def()
3101 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
3102 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
3103 # handle #Object.func()
3104 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
3106 # Convert '@param', but not '\@param'.
3107 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
3108 $text =~ s/\\\@/\@/g;
3110 # Convert '%constant', but not '\%constant'.
3111 # Also allow negative numbers, e.g. %-1.
3112 $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
3113 $text =~ s/\\\%/\%/g;
3115 # Convert '#symbol', but not '\#symbol'.
3116 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
3123 # This is called inside a <programlisting>
3124 sub ExpandAbbreviationsCallback2 {
3125 my ($text, $symbol, $tag) = @_;
3128 # We are inside a <programlisting> but outside any CDATA sections,
3129 # so we expand any gtk-doc abbreviations.
3130 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
3131 # why not just call it
3132 $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
3133 } elsif ($tag eq "<![CDATA[") {
3134 # NOTE: this is a fallback. It is normally done by the Markdown parser.
3135 $text = &ReplaceEntities ($text, $symbol);
3142 my ($symbol, $tag) = @_;;
3145 # Check for things like '#include', '#define', and skip them.
3146 if ($PreProcessorDirectives{$symbol}) {
3150 # Get rid of special suffixes ('-struct','-enum').
3151 $text =~ s/-struct$//;
3152 $text =~ s/-enum$//;
3154 # If the symbol is in the form "Object::signal", then change the symbol to
3155 # "Object-signal" and use "signal" as the text.
3156 if ($symbol =~ s/::/-/) {
3160 # If the symbol is in the form "Object:property", then change the symbol to
3161 # "Object--property" and use "property" as the text.
3162 if ($symbol =~ s/:/--/) {
3167 $text = tagify ($text, $tag);
3170 return &MakeXRef($symbol, $text);
3174 #############################################################################
3175 # Function : ModifyXMLElements
3176 # Description : Looks for given XML element tags within the text, and calls
3177 # the callback on pieces of text inside & outside those elements.
3178 # Used for special handling of text inside things like CDATA
3179 # and <programlisting>.
3180 # Arguments : $text - the text.
3181 # $symbol - the symbol currently being documented (only used for
3183 # $start_tag_regexp - the regular expression to match start tags.
3184 # e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3185 # CDATA sections or programlisting elements.
3186 # $end_tag_func - function which is passed the matched start tag
3187 # and should return the appropriate end tag string regexp.
3188 # $callback - callback called with each part of the text. It is
3189 # called with a piece of text, the symbol being
3190 # documented, and the matched start tag or "" if the text
3191 # is outside the XML elements being matched.
3192 #############################################################################
3193 sub ModifyXMLElements {
3194 my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3195 my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3198 while ($text =~ m/$start_tag_regexp/s) {
3199 $before_tag = $`; # Prematch for last successful match string
3200 $start_tag = $&; # Last successful match
3201 $text = $'; # Postmatch for last successful match string
3203 $result .= &$callback ($before_tag, $symbol, "");
3204 $result .= $start_tag;
3206 # get the matching end-tag for current tag
3207 $end_tag_regexp = &$end_tag_func ($start_tag);
3209 if ($text =~ m/$end_tag_regexp/s) {
3214 $result .= &$callback ($before_tag, $symbol, $start_tag);
3215 $result .= $end_tag;
3217 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3218 "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3219 # Just assume it is all inside the tag.
3220 $result .= &$callback ($text, $symbol, $start_tag);
3225 # Handle any remaining text outside the tags.
3226 $result .= &$callback ($text, $symbol, "");
3235 # Adds a tag around some text.
3236 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3238 my ($text, $elem) = @_;
3239 return "<" . $elem . ">" . $text . "</" . $elem . ">";
3242 #############################################################################
3243 # Function : MakeDocHeader
3244 # Description : Builds a docbook header for the given tag
3245 # Arguments : $tag - doctype tag
3246 #############################################################################
3250 my $header = $doctype_header;
3251 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE $tag/;
3253 # fix the path for book since this is one level up
3254 if ($tag eq "book") {
3255 $header =~ s#<!ENTITY % gtkdocentities SYSTEM \"../([a-zA-Z./]+)\">#<!ENTITY % gtkdocentities SYSTEM \"$1\">#;
3262 #############################################################################
3263 # Function : MakeXRef
3264 # Description : This returns a cross-reference link to the given symbol.
3265 # Though it doesn't try to do this for a few standard C types
3266 # that it knows won't be in the documentation.
3267 # Arguments : $symbol - the symbol to try to create a XRef to.
3268 # $text - text text to put inside the XRef, defaults to $symbol
3269 #############################################################################
3272 my ($symbol, $text) = ($_[0], $_[1]);
3274 $symbol =~ s/^\s+//;
3275 $symbol =~ s/\s+$//;
3277 if (!defined($text)) {
3280 # Get rid of special suffixes ('-struct','-enum').
3281 $text =~ s/-struct$//;
3282 $text =~ s/-enum$//;
3285 if ($symbol =~ m/ /) {
3289 @TRACE@("Getting type link for $symbol -> $text\n");
3291 my $symbol_id = &CreateValidSGMLID ($symbol);
3292 return "<link linkend=\"$symbol_id\">$text</link>";
3296 #############################################################################
3297 # Function : MakeIndexterms
3298 # Description : This returns a indexterm elements for the given symbol
3299 # Arguments : $symbol - the symbol to create indexterms for
3300 #############################################################################
3302 sub MakeIndexterms {
3303 my ($symbol, $id) = @_;
3307 # make the index useful, by ommiting the namespace when sorting
3308 if ($NAME_SPACE ne "") {
3309 if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3310 $sortas=" sortas=\"$1\"";
3314 if (exists $Deprecated{$symbol}) {
3315 $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3316 $IndexEntriesDeprecated{$symbol}=$id;
3317 $IndexEntriesFull{$symbol}=$id;
3319 if (exists $Since{$symbol}) {
3320 my $since = $Since{$symbol};
3324 $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3326 $IndexEntriesSince{$symbol}=$id;
3327 $IndexEntriesFull{$symbol}=$id;
3330 $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3331 $IndexEntriesFull{$symbol}=$id;
3337 #############################################################################
3338 # Function : MakeDeprecationNote
3339 # Description : This returns a deprecation warning for the given symbol.
3340 # Arguments : $symbol - the symbol to try to create a warning for.
3341 #############################################################################
3343 sub MakeDeprecationNote {
3344 my ($symbol) = $_[0];
3346 if (exists $Deprecated{$symbol}) {
3349 $desc .= "<warning><para><literal>$symbol</literal> ";
3351 $note = $Deprecated{$symbol};
3353 if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3354 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3356 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3358 $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3362 $note = &ConvertMarkDown($symbol, $note);
3363 $desc .= " " . $note;
3365 $desc .= "</warning>\n";
3370 #############################################################################
3371 # Function : MakeConditionDescription
3372 # Description : This returns a sumary of conditions for the given symbol.
3373 # Arguments : $symbol - the symbol to try to create the sumary.
3374 #############################################################################
3376 sub MakeConditionDescription {
3377 my ($symbol) = $_[0];
3380 if (exists $Deprecated{$symbol}) {
3385 if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3386 $desc .= "deprecated:$1";
3388 $desc .= "deprecated";
3392 if (exists $Since{$symbol}) {
3397 if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3398 $desc .= "since:$1";
3404 if (exists $StabilityLevel{$symbol}) {
3408 $desc .= "stability:".$StabilityLevel{$symbol};
3413 $cond =~ s/\"/"/g;
3414 $desc=" condition=\"".$cond."\"";
3415 @TRACE@("condition for '$symbol' = '$desc'\n");
3420 #############################################################################
3421 # Function : GetHierarchy
3422 # Description : Returns the DocBook output describing the ancestors and
3423 # immediate children of a GObject subclass. It uses the
3424 # global @Objects and @ObjectLevels arrays to walk the tree.
3426 # Arguments : $object - the GtkObject subclass.
3427 # @hierarchy - previous hierarchy
3428 #############################################################################
3431 my ($object,$hierarchy_ref) = @_;
3432 my @hierarchy = @{$hierarchy_ref};
3434 # Find object in the objects array.
3440 for ($i = 0; $i < @Objects; $i++) {
3442 if ($ObjectLevels[$i] <= $level) {
3445 elsif ($ObjectLevels[$i] == $level + 1) {
3446 push (@children, $Objects[$i]);
3449 elsif ($Objects[$i] eq $object) {
3452 $level = $ObjectLevels[$i];
3459 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3461 push (@ancestors, $object);
3462 @TRACE@("Level: $level\n");
3463 while ($level > 1) {
3465 if ($ObjectLevels[$j] < $level) {
3466 push (@ancestors, $Objects[$j]);
3467 $level = $ObjectLevels[$j];
3468 @TRACE@("Level: $level\n");
3472 # Output the ancestors, indented and with links.
3475 for ($i = $#ancestors; $i >= 0; $i--) {
3478 my $ancestor = $ancestors[$i];
3479 my $ancestor_id = &CreateValidSGMLID ($ancestor);
3480 my $indent = ' ' x ($level * 4);
3481 # Don't add a link to the current object, i.e. when i == 0.
3483 $entry_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3484 $alt_text = $indent . $ancestor;
3486 $entry_text = $indent . $ancestor;
3487 $alt_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3489 @TRACE@("Checking for '$entry_text' or '$alt_text'");
3490 # Check if we already have this object
3492 for ($j = 0; $j <= $#hierarchy; $j++) {
3493 if (($hierarchy[$j] eq $entry_text) or ($hierarchy[$j] eq $alt_text)) {
3499 # We have a new entry, find insert position in alphabetical order
3501 for ($j = $last_index; $j <= $#hierarchy; $j++) {
3502 if ($hierarchy[$j] !~ m/^${indent}/) {
3506 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3507 my $stripped_text = $hierarchy[$j];
3508 if ($entry_text !~ m/<link linkend/) {
3509 $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3510 $stripped_text =~ s%</link>%%;
3512 if ($entry_text lt $stripped_text) {
3521 $last_index = 1 + $#hierarchy;
3523 splice @hierarchy, $last_index, 0, ($entry_text);
3526 # Already have this one, make sure we use the not linked version
3527 if ($entry_text !~ m/<link linkend=/) {
3528 $hierarchy[$j] = $entry_text;
3530 # Remember index as base insert point
3531 $last_index = $index + 1;
3535 # Output the children, indented and with links.
3536 for ($i = 0; $i <= $#children; $i++) {
3537 my $id = &CreateValidSGMLID ($children[$i]);
3538 my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3539 splice @hierarchy, $last_index, 0, ($indented_text);
3546 #############################################################################
3547 # Function : GetInterfaces
3548 # Description : Returns the DocBook output describing the interfaces
3549 # implemented by a class. It uses the global %Interfaces hash.
3550 # Arguments : $object - the GtkObject subclass.
3551 #############################################################################
3558 # Find object in the objects array.
3559 if (exists($Interfaces{$object})) {
3560 my @ifaces = split(' ', $Interfaces{$object});
3565 for ($i = 0; $i <= $#ifaces; $i++) {
3566 my $id = &CreateValidSGMLID ($ifaces[$i]);
3567 $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3568 if ($i < $#ifaces - 1) {
3571 elsif ($i < $#ifaces) {
3586 #############################################################################
3587 # Function : GetImplementations
3588 # Description : Returns the DocBook output describing the implementations
3589 # of an interface. It uses the global %Interfaces hash.
3590 # Arguments : $object - the GtkObject subclass.
3591 #############################################################################
3593 sub GetImplementations {
3598 foreach my $key (keys %Interfaces) {
3599 if ($Interfaces{$key} =~ /\b$object\b/) {
3600 push (@impls, $key);
3604 @impls = sort @impls;
3607 $object is implemented by
3609 for ($i = 0; $i <= $#impls; $i++) {
3610 my $id = &CreateValidSGMLID ($impls[$i]);
3611 $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3612 if ($i < $#impls - 1) {
3615 elsif ($i < $#impls) {
3630 #############################################################################
3631 # Function : GetPrerequisites
3632 # Description : Returns the DocBook output describing the prerequisites
3633 # of an interface. It uses the global %Prerequisites hash.
3634 # Arguments : $iface - the interface.
3635 #############################################################################
3637 sub GetPrerequisites {
3642 if (exists($Prerequisites{$iface})) {
3647 my @prereqs = split(' ', $Prerequisites{$iface});
3648 for ($i = 0; $i <= $#prereqs; $i++) {
3649 my $id = &CreateValidSGMLID ($prereqs[$i]);
3650 $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3651 if ($i < $#prereqs - 1) {
3654 elsif ($i < $#prereqs) {
3668 #############################################################################
3669 # Function : GetDerived
3670 # Description : Returns the DocBook output describing the derived interfaces
3671 # of an interface. It uses the global %Prerequisites hash.
3672 # Arguments : $iface - the interface.
3673 #############################################################################
3681 foreach my $key (keys %Prerequisites) {
3682 if ($Prerequisites{$key} =~ /\b$iface\b/) {
3683 push (@derived, $key);
3686 if ($#derived >= 0) {
3687 @derived = sort @derived;
3690 $iface is required by
3692 for ($i = 0; $i <= $#derived; $i++) {
3693 my $id = &CreateValidSGMLID ($derived[$i]);
3694 $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3695 if ($i < $#derived - 1) {
3698 elsif ($i < $#derived) {
3713 #############################################################################
3714 # Function : GetSignals
3715 # Description : Returns the synopsis and detailed description DocBook output
3716 # for the signal handlers of a given GtkObject subclass.
3717 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3718 #############################################################################
3726 for ($i = 0; $i <= $#SignalObjects; $i++) {
3727 if ($SignalObjects[$i] eq $object) {
3728 @TRACE@("Found signal: $SignalNames[$i]\n");
3729 my $name = $SignalNames[$i];
3730 my $symbol = "${object}::${name}";
3731 my $id = &CreateValidSGMLID ("$object-$name");
3733 $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3734 $desc .= MakeIndexterms($symbol, $id);
3736 $desc .= OutputSymbolExtraLinks($symbol);
3738 $desc .= "<programlisting language=\"C\">";
3740 $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3741 my $type_modifier = defined($1) ? $1 : "";
3744 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3746 my $ret_type_output = "$type_modifier$xref$pointer";
3747 my $callback_name = "user_function";
3748 $desc .= "${ret_type_output}\n${callback_name} (";
3750 my $indentation = ' ' x (length($callback_name) + 2);
3751 my $pad = $indentation;
3753 my $sourceparams = $SourceSymbolParams{$symbol};
3754 my @params = split ("\n", $SignalPrototypes[$i]);
3757 my $type_len = length("gpointer");
3758 my $name_len = length("user_data");
3759 # do two passes, the first one is to calculate padding
3760 for ($l = 0; $l < 2; $l++) {
3761 for ($j = 0; $j <= $#params; $j++) {
3763 # allow alphanumerics, '_', '[' & ']' in param names
3764 if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3767 if (defined($sourceparams)) {
3768 $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3773 if (!defined($param_name)) {
3774 $param_name = "arg$j";
3777 if (length($type) + length($pointer) > $type_len) {
3778 $type_len = length($type) + length($pointer);
3780 if (length($param_name) > $name_len) {
3781 $name_len = length($param_name);
3785 $xref = &MakeXRef ($type, &tagify($type, "type"));
3786 $pad = ' ' x ($type_len - length($type) - length($pointer));
3787 $desc .= "$xref$pad $pointer${param_name},\n";
3788 $desc .= $indentation;
3791 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3792 "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3796 $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3797 $pad = ' ' x ($type_len - length("gpointer"));
3798 $desc .= "$xref$pad user_data)";
3799 $desc .= "</programlisting>\n";
3801 my $flags = $SignalFlags[$i];
3802 my $flags_string = "";
3804 if (defined ($flags)) {
3805 if ($flags =~ m/f/) {
3806 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3808 elsif ($flags =~ m/l/) {
3809 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3811 elsif ($flags =~ m/c/) {
3812 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3813 $flags_string = "Cleanup";
3815 if ($flags =~ m/r/) {
3816 if ($flags_string) { $flags_string .= " / "; }
3817 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3819 if ($flags =~ m/d/) {
3820 if ($flags_string) { $flags_string .= " / "; }
3821 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3823 if ($flags =~ m/a/) {
3824 if ($flags_string) { $flags_string .= " / "; }
3825 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3827 if ($flags =~ m/h/) {
3828 if ($flags_string) { $flags_string .= " / "; }
3829 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3833 $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";
3835 my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3837 $AllSymbols{$symbol} = 1;
3838 if (defined ($SymbolDocs{$symbol})) {
3839 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3841 $desc .= $symbol_docs;
3843 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3844 $AllDocumentedSymbols{$symbol} = 1;
3847 if (defined ($SymbolAnnotations{$symbol})) {
3848 my $param_desc = $SymbolAnnotations{$symbol};
3849 my $param_annotations = "";
3850 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3851 if ($param_annotations ne "") {
3852 $desc .= "\n<para>$param_annotations</para>";
3855 $desc .= &MakeDeprecationNote($symbol);
3857 $desc .= $parameters;
3858 if ($flags_string) {
3859 $desc .= "<para>Flags: $flags_string</para>\n";
3861 $desc .= OutputSymbolTraits ($symbol);
3862 $desc .= "</refsect2>";
3865 return ($synop, $desc);
3869 #############################################################################
3870 # Function : GetArgs
3871 # Description : Returns the synopsis and detailed description DocBook output
3872 # for the Args of a given GtkObject subclass.
3873 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3874 #############################################################################
3880 my $child_synop = "";
3881 my $child_desc = "";
3882 my $style_synop = "";
3883 my $style_desc = "";
3886 for ($i = 0; $i <= $#ArgObjects; $i++) {
3887 if ($ArgObjects[$i] eq $object) {
3888 @TRACE@("Found arg: $ArgNames[$i]\n");
3889 my $name = $ArgNames[$i];
3890 my $flags = $ArgFlags[$i];
3891 my $flags_string = "";
3895 if ($flags =~ m/c/) {
3896 $kind = "child property";
3899 elsif ($flags =~ m/s/) {
3900 $kind = "style property";
3907 # Remember only one colon so we don't clash with signals.
3908 my $symbol = "${object}:${name}";
3909 # use two dashes and ev. an extra separator here for the same reason.
3910 my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3912 my $type = $ArgTypes[$i];
3914 my $range = $ArgRanges[$i];
3915 my $range_output = CreateValidSGML ($range);
3916 my $default = $ArgDefaults[$i];
3917 my $default_output = CreateValidSGML ($default);
3919 if ($type eq "GtkString") {
3920 $type = "char *";
3922 if ($type eq "GtkSignal") {
3923 $type = "GtkSignalFunc, gpointer";
3924 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3925 . &MakeXRef ("gpointer");
3926 } elsif ($type =~ m/^(\w+)\*$/) {
3927 $type_output = &MakeXRef ($1, &tagify($1, "type")) . " *";
3929 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3932 if ($flags =~ m/r/) {
3933 $flags_string = "Read";
3935 if ($flags =~ m/w/) {
3936 if ($flags_string) { $flags_string .= " / "; }
3937 $flags_string .= "Write";
3939 if ($flags =~ m/x/) {
3940 if ($flags_string) { $flags_string .= " / "; }
3941 $flags_string .= "Construct";
3943 if ($flags =~ m/X/) {
3944 if ($flags_string) { $flags_string .= " / "; }
3945 $flags_string .= "Construct Only";
3948 $AllSymbols{$symbol} = 1;
3950 if (defined($SymbolDocs{$symbol}) &&
3951 !IsEmptyDoc($SymbolDocs{$symbol})) {
3952 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3953 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3954 $AllDocumentedSymbols{$symbol} = 1;
3957 if ($ArgBlurbs[$i] ne "") {
3958 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3959 $AllDocumentedSymbols{$symbol} = 1;
3961 # FIXME: print a warning?
3962 @TRACE@(".. no description\n");
3967 if (length ($name) < 24) {
3968 $pad1 = " " x (24 - length ($name));
3971 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";
3972 my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3973 $arg_desc .= MakeIndexterms($symbol, $id);
3975 $arg_desc .= OutputSymbolExtraLinks($symbol);
3977 $arg_desc .= "<programlisting> “$name”$pad1 $type_output</programlisting>\n";
3978 $arg_desc .= $blurb;
3979 if (defined ($SymbolAnnotations{$symbol})) {
3980 my $param_desc = $SymbolAnnotations{$symbol};
3981 my $param_annotations = "";
3982 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3983 if ($param_annotations ne "") {
3984 $arg_desc .= "\n<para>$param_annotations</para>";
3987 $arg_desc .= &MakeDeprecationNote($symbol);
3989 if ($flags_string) {
3990 $arg_desc .= "<para>Flags: $flags_string</para>\n";
3993 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3995 if ($default ne "") {
3996 $arg_desc .= "<para>Default value: $default_output</para>\n";
3998 $arg_desc .= OutputSymbolTraits ($symbol);
3999 $arg_desc .= "</refsect2>\n";
4001 if ($flags =~ m/c/) {
4002 $child_synop .= $arg_synop;
4003 $child_desc .= $arg_desc;
4005 elsif ($flags =~ m/s/) {
4006 $style_synop .= $arg_synop;
4007 $style_desc .= $arg_desc;
4010 $synop .= $arg_synop;
4015 return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
4019 #############################################################################
4020 # Function : ReadSourceDocumentation
4021 # Description : This reads in the documentation embedded in comment blocks
4022 # in the source code (for Gnome).
4024 # Parameter descriptions override any in the template files.
4025 # Function descriptions are placed before any description from
4026 # the template files.
4028 # It recursively descends the source directory looking for .c
4029 # files and scans them looking for specially-formatted comment
4032 # Arguments : $source_dir - the directory to scan.
4033 #############m###############################################################
4035 sub ReadSourceDocumentation {
4036 my ($source_dir) = @_;
4037 my ($file, $dir, @suffix_list, $suffix);
4039 # prepend entries from @SOURCE_DIR
4040 for my $dir (@SOURCE_DIRS) {
4041 # Check if the filename is in the ignore list.
4042 if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
4043 @TRACE@("Skipping source directory: $source_dir");
4046 @TRACE@("No match for: ".($1 || $source_dir));
4050 @TRACE@("Scanning source directory: $source_dir");
4052 # This array holds any subdirectories found.
4055 @suffix_list = split (/,/, $SOURCE_SUFFIXES);
4057 opendir (SRCDIR, $source_dir)
4058 || die "Can't open source directory $source_dir: $!";
4060 foreach $file (readdir (SRCDIR)) {
4061 if ($file =~ /^\./) {
4063 } elsif (-d "$source_dir/$file") {
4064 push (@subdirs, $file);
4065 } elsif (@suffix_list) {
4066 foreach $suffix (@suffix_list) {
4067 if ($file =~ m/\.\Q${suffix}\E$/) {
4068 &ScanSourceFile ("$source_dir/$file");
4071 } elsif ($file =~ m/\.[ch]$/) {
4072 &ScanSourceFile ("$source_dir/$file");
4077 # Now recursively scan the subdirectories.
4078 foreach $dir (@subdirs) {
4079 &ReadSourceDocumentation ("$source_dir/$dir");
4084 #############################################################################
4085 # Function : ScanSourceFile
4086 # Description : Scans one source file looking for specially-formatted comment
4087 # blocks. Later &MergeSourceDocumentation is used to merge any
4088 # documentation found with the documentation already read in
4089 # from the template files.
4091 # Arguments : $file - the file to scan.
4092 #############################################################################
4094 sub ScanSourceFile {
4098 # prepend entries from @SOURCE_DIR
4099 for my $dir (@SOURCE_DIRS) {
4100 # Check if the filename is in the ignore list.
4101 if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
4102 @TRACE@("Skipping source file: $file");
4107 if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
4110 &LogWarning ($file, 1, "Can't find basename for this filename.");
4114 # Check if the basename is in the list of files to ignore.
4115 if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
4116 @TRACE@("Skipping source file: $file");
4120 @TRACE@("Scanning source file: $file");
4122 open (SRCFILE, $file)
4123 || die "Can't open $file: $!";
4124 my $in_comment_block = 0;
4127 my ($description, $return_desc);
4128 my ($since_desc, $stability_desc, $deprecated_desc);
4132 # Look for the start of a comment block.
4133 if (!$in_comment_block) {
4134 if (m%^\s*/\*.*\*/%) {
4135 #one-line comment - not gtkdoc
4136 } elsif (m%^\s*/\*\*\s%) {
4137 @TRACE@("Found comment block start\n");
4139 $in_comment_block = 1;
4141 # Reset all the symbol data.
4147 $deprecated_desc = "";
4148 $stability_desc = "";
4149 $current_param = -1;
4155 # We're in a comment block. Check if we've found the end of it.
4158 # maybe its not even meant to be a gtk-doc comment?
4159 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
4161 # Add the return value description onto the end of the params.
4163 # TODO(ensonic): check for duplicated Return docs
4164 # &LogWarning ($file, $., "Multiple Returns for $symbol.");
4165 push (@params, "Returns");
4166 push (@params, $return_desc);
4168 # Convert special characters
4169 $description = &ConvertSGMLChars ($symbol, $description);
4171 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4172 $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
4175 # Handle Section docs
4176 if ($symbol =~ m/SECTION:\s*(.*)/) {
4180 if (scalar %KnownSymbols) {
4181 if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
4182 &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
4186 @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
4187 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4188 @TRACE@(" '".$params[$k]."'\n");
4189 $params[$k] = "\L$params[$k]";
4191 if ($params[$k] eq "short_description") {
4192 $key = "$TMPL_DIR/$real_symbol:Short_Description";
4193 } elsif ($params[$k] eq "see_also") {
4194 $key = "$TMPL_DIR/$real_symbol:See_Also";
4195 } elsif ($params[$k] eq "title") {
4196 $key = "$TMPL_DIR/$real_symbol:Title";
4197 } elsif ($params[$k] eq "stability") {
4198 $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4199 } elsif ($params[$k] eq "section_id") {
4200 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4201 } elsif ($params[$k] eq "include") {
4202 $key = "$TMPL_DIR/$real_symbol:Include";
4203 } elsif ($params[$k] eq "image") {
4204 $key = "$TMPL_DIR/$real_symbol:Image";
4206 if (defined($key)) {
4207 $SourceSymbolDocs{$key}=$params[$k+1];
4208 $SourceSymbolSourceFile{$key} = $file;
4209 $SourceSymbolSourceLine{$key} = $.;
4212 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4213 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4214 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4215 #$SourceSymbolTypes{$symbol} = "SECTION";
4216 } elsif ($symbol =~ m/PROGRAM:\s*(.*)/) {
4217 my $real_symbol = $1;
4221 @TRACE@("PROGRAM DOCS found in source for '$real_symbol'\n");
4222 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4223 @TRACE@(" '".$params[$k]."'\n");
4224 $params[$k] = "\L$params[$k]";
4227 if ($params[$k] eq "short_description") {
4228 $key = "$TMPL_DIR/$real_symbol:Short_Description";
4229 } elsif ($params[$k] eq "see_also") {
4230 $key = "$TMPL_DIR/$real_symbol:See_Also";
4231 } elsif ($params[$k] eq "section_id") {
4232 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4233 } elsif ($params[$k] eq "synopsis") {
4234 $key = "$TMPL_DIR/$real_symbol:Synopsis";
4235 } elsif ($params[$k] eq "returns") {
4236 $key = "$TMPL_DIR/$real_symbol:Returns";
4237 } elsif ($params[$k] =~ m/^(-.*)/) {
4238 $key = "$TMPL_DIR/$real_symbol:Options";
4240 if (defined($SourceSymbolDocs{$key})) {
4241 $opts = $SourceSymbolDocs{$key};
4245 push (@{ $opts }, $1);
4246 push (@{ $opts }, $params[$k+1]);
4248 $SourceSymbolDocs{$key} = $opts;
4251 if (defined($key)) {
4252 $SourceSymbolDocs{$key}=$params[$k+1];
4253 $SourceSymbolSourceFile{$key} = $file;
4254 $SourceSymbolSourceLine{$key} = $.;
4257 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4258 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4259 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4261 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Section_Id"};
4262 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
4263 # Remove trailing blanks and use as is
4264 $section_id =~ s/\s+$//;
4266 $section_id = &CreateValidSGMLID ("$MODULE-$real_symbol");
4269 &OutputProgramDBFile ($real_symbol, $section_id);
4272 @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4273 $SourceSymbolDocs{$symbol} = $description;
4274 $SourceSymbolParams{$symbol} = [ @params ];
4275 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4276 #if (defined $DeclarationTypes{$symbol}) {
4277 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4279 $SourceSymbolSourceFile{$symbol} = $file;
4280 $SourceSymbolSourceLine{$symbol} = $.;
4284 ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4285 $since_desc =~ s/^\s+//;
4286 $since_desc =~ s/\s+$//;
4287 @TRACE@("Since($symbol) : [$since_desc]\n");
4288 $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4289 if(scalar @extra_lines) {
4290 &LogWarning ($file, $., "multi-line since docs found");
4294 if ($stability_desc) {
4295 $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4296 $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4299 if ($deprecated_desc) {
4300 if (!exists $Deprecated{$symbol}) {
4301 # don't warn for signals and properties
4302 #if ($symbol !~ m/::?(.*)/) {
4303 if (defined $DeclarationTypes{$symbol}) {
4304 &LogWarning ($file, $.,
4305 "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4306 " (See the --deprecated-guards option for gtkdoc-scan.)");
4309 $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4313 $in_comment_block = 0;
4317 # Get rid of ' * ' at start of every line in the comment block.
4319 # But make sure we don't get rid of the newline at the end.
4323 @TRACE@("scanning :$_");
4325 # If we haven't found the symbol name yet, look for it.
4327 if (m%^\s*(SECTION:\s*\S+)%) {
4329 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4330 } elsif (m%^\s*(PROGRAM:\s*\S+)%) {
4332 @TRACE@("PROGRAM DOCS found in source for : '$symbol'\n");
4333 } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._() ]+?\)\s*)*$%) {
4335 my $annotation = $2;
4336 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4337 if (defined($annotation)) {
4339 if ($annotation ne "") {
4340 $SymbolAnnotations{$symbol} = $annotation;
4341 @TRACE@("remaining text for $symbol: '$annotation'\n");
4348 if ($in_part eq "description") {
4349 # Get rid of 'Description:'
4350 s%^\s*Description:%%;
4353 if (m%^\s*(returns|return\s+value):%i) {
4354 # we're in param section and have not seen the blank line
4355 if($in_part ne "") {
4357 $in_part = "return";
4360 } elsif (m%^\s*since:%i) {
4361 # we're in param section and have not seen the blank line
4362 if($in_part ne "param") {
4367 } elsif (m%^\s*deprecated:%i) {
4368 # we're in param section and have not seen the blank line
4369 if($in_part ne "param") {
4370 $deprecated_desc = $';
4371 $in_part = "deprecated";
4374 } elsif (m%^\s*stability:%i) {
4375 $stability_desc = $';
4376 $in_part = "stability";
4380 if ($in_part eq "description") {
4383 } elsif ($in_part eq "return") {
4386 } elsif ($in_part eq "since") {
4389 } elsif ($in_part eq "stability") {
4390 $stability_desc .= $_;
4392 } elsif ($in_part eq "deprecated") {
4393 $deprecated_desc .= $_;
4397 # We must be in the parameters. Check for the empty line below them.
4399 $in_part = "description";
4403 # Look for a parameter name.
4404 if (m%^\s*@(.+?)\s*:\s*%) {
4405 my $param_name = $1;
4406 my $param_desc = $';
4408 @TRACE@("Found parameter: $param_name\n");
4409 # Allow varargs variations
4410 if ($param_name =~ m/^\.\.\.$/) {
4411 $param_name = "...";
4413 @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4415 push (@params, $param_name);
4416 push (@params, $param_desc);
4417 $current_param += $PARAM_FIELD_COUNT;
4420 } elsif ($in_part eq "") {
4421 @TRACE@("continuation for $symbol annotation '$_'");
4422 my $annotation = $_;
4423 $annotation =~ s/^\s+|\s+$//g ;
4424 $SymbolAnnotations{$symbol} .= $annotation;
4428 # We must be in the middle of a parameter description, so add it on
4429 # to the last element in @params.
4430 if ($current_param == -1) {
4431 &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
4433 $params[$#params] .= $_;
4439 #############################################################################
4440 # Function : OutputMissingDocumentation
4441 # Description : Outputs report of documentation coverage to a file
4444 #############################################################################
4446 sub OutputMissingDocumentation {
4447 my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4448 my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4450 my $n_documented = 0;
4451 my $n_incomplete = 0;
4457 my $buffer_deprecated = "";
4458 my $buffer_descriptions = "";
4460 open(UNDOCUMENTED, ">$new_undocumented_file")
4461 || die "Can't create $new_undocumented_file";
4463 foreach $symbol (sort (keys (%AllSymbols))) {
4464 # FIXME: should we print LogWarnings for undocumented stuff?
4466 #my $ssfile = &GetSymbolSourceFile($symbol);
4467 #my $ssline = &GetSymbolSourceLine($symbol);
4468 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4470 if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4472 if (exists ($AllDocumentedSymbols{$symbol})) {
4474 if (exists ($AllIncompleteSymbols{$symbol})) {
4476 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4477 #$buffer .= "\t0: ".$location;
4479 } elsif (exists $Deprecated{$symbol}) {
4480 if (exists ($AllIncompleteSymbols{$symbol})) {
4482 $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4483 #$buffer .= "\t1a: ".$location;
4485 $buffer_deprecated .= $symbol . "\n";
4486 #$buffer .= "\t1b: ".$location;
4489 if (exists ($AllIncompleteSymbols{$symbol})) {
4491 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4492 #$buffer .= "\t2a: ".$location;
4494 $buffer .= $symbol . "\n";
4495 #$buffer .= "\t2b: ".$location;
4498 } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4500 if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4501 || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4504 # cut off the leading namespace ($TMPL_DIR)
4505 $symbol =~ m/^.*\/(.*)$/;
4506 $buffer_descriptions .= $1 . "\n";
4514 $percent = ($n_documented / $total) * 100.0;
4517 printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4518 print UNDOCUMENTED "$n_documented symbols documented.\n";
4519 print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4520 print UNDOCUMENTED ($total - $n_documented) . " not documented.\n";
4522 if ($buffer_deprecated ne "") {
4523 $buffer .= "\n" . $buffer_deprecated;
4525 if ($buffer_descriptions ne "") {
4526 $buffer .= "\n" . $buffer_descriptions;
4528 if ($buffer ne "") {
4529 print UNDOCUMENTED "\n\n$buffer";
4531 close (UNDOCUMENTED);
4533 return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4535 printf "%.0f%% symbol docs coverage", $percent;
4536 print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4537 print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4541 #############################################################################
4542 # Function : OutputUndeclaredSymbols
4543 # Description : Outputs symbols that are listed in the section file, but not
4544 # declaration is found in the sources
4547 #############################################################################
4549 sub OutputUndeclaredSymbols {
4550 my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4551 my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4553 open(UNDECLARED, ">$new_undeclared_file")
4554 || die "Can't create $new_undeclared_file";
4556 if (%UndeclaredSymbols) {
4557 print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4558 print UNDECLARED "\n";
4559 print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4563 return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4566 #############################################################################
4567 # Function : OutputUnusedSymbols
4568 # Description : Outputs symbols that are documented in comments, but not
4569 # declared in the sources
4572 #############################################################################
4574 sub OutputUnusedSymbols {
4576 my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4577 my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4579 open (UNUSED, ">$new_unused_file")
4580 || die "Can't open $new_unused_file";
4582 foreach $symbol (sort keys (%Declarations)) {
4583 if (!defined ($DeclarationOutput{$symbol})) {
4584 print (UNUSED "$symbol\n");
4588 foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4589 print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4593 if ($num_unused != 0) {
4594 &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4595 "They should be added to $MODULE-sections.txt in the appropriate place.");
4598 return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4602 #############################################################################
4603 # Function : OutputAllSymbols
4604 # Description : Outputs list of all symbols to a file
4607 #############################################################################
4609 sub OutputAllSymbols {
4610 my $n_documented = 0;
4616 open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4617 || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4619 foreach $symbol (sort (keys (%AllSymbols))) {
4620 print SYMBOLS $symbol . "\n";
4626 #############################################################################
4627 # Function : OutputSymbolsWithoutSince
4628 # Description : Outputs list of all symbols without a since tag to a file
4631 #############################################################################
4633 sub OutputSymbolsWithoutSince {
4634 my $n_documented = 0;
4640 open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4641 || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4643 foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4644 if (!defined $Since{$symbol}) {
4645 print SYMBOLS $symbol . "\n";
4653 #############################################################################
4654 # Function : MergeSourceDocumentation
4655 # Description : This merges documentation read from a source file into the
4656 # documentation read in from a template file.
4658 # Parameter descriptions override any in the template files.
4659 # Function descriptions are placed before any description from
4660 # the template files.
4663 #############################################################################
4665 sub MergeSourceDocumentation {
4669 if (scalar %SymbolDocs) {
4670 @Symbols=keys (%SymbolDocs);
4671 @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4674 # filter scanned declarations, with what we suppress from -sections.txt
4676 foreach $symbol (keys (%Declarations)) {
4677 if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4681 # , add the rest from -sections.txt
4682 foreach $symbol (keys (%KnownSymbols)) {
4683 if ($KnownSymbols{$symbol} == 1) {
4687 # and add whats found in the source
4688 foreach $symbol (keys (%SourceSymbolDocs)) {
4691 @Symbols = keys (%tmp);
4692 @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4694 foreach $symbol (@Symbols) {
4695 $AllSymbols{$symbol} = 1;
4697 my $have_tmpl_docs = 0;
4699 ## see if the symbol is documented in template
4700 my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4701 my $check_tmpl_doc =$tmpl_doc;
4702 # remove all xml-tags and whitespaces
4703 $check_tmpl_doc =~ s/<.*?>//g;
4704 $check_tmpl_doc =~ s/\s//g;
4706 if ($check_tmpl_doc ne "") {
4707 $have_tmpl_docs = 1;
4709 # if the docs have just an empty para, don't merge that.
4710 $check_tmpl_doc = $tmpl_doc;
4711 $check_tmpl_doc =~ s/(\s|\n)//msg;
4712 if ($check_tmpl_doc eq "<para></para>") {
4717 if (exists ($SourceSymbolDocs{$symbol})) {
4718 my $type = $DeclarationTypes {$symbol};
4720 @TRACE@("merging [$symbol] from source\n");
4722 my $item = "Parameter";
4723 if (defined ($type)) {
4724 if ($type eq 'STRUCT') {
4726 } elsif ($type eq 'ENUM') {
4728 } elsif ($type eq 'UNION') {
4735 my $src_doc = $SourceSymbolDocs{$symbol};
4736 # remove leading and training whitespaces
4737 $src_doc =~ s/^\s+//;
4738 $src_doc =~ s/\s+$//;
4740 # Don't output warnings for overridden titles as titles are
4741 # automatically generated in the -sections.txt file, and thus they
4742 # are often overridden.
4743 if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4744 # check if content is different
4745 if ($tmpl_doc ne $src_doc) {
4746 #print "[$tmpl_doc] [$src_doc]\n";
4747 &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4748 "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4752 if ($src_doc ne "") {
4753 $AllDocumentedSymbols{$symbol} = 1;
4756 # Do not add <para> to nothing, it breaks missing docs checks.
4757 my $src_doc_para = "";
4758 if ($src_doc ne "") {
4759 $src_doc_para = $src_doc;
4762 if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4763 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4764 } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4765 # For the title/summary/see also section docs we don't want to
4766 # add any <para> tags.
4767 $SymbolDocs{$symbol} = "$src_doc"
4769 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4773 if ($symbol =~ m/.*::.*/) {
4774 # For signals we prefer the param names from the source docs,
4775 # since the ones from the templates are likely to contain the
4776 # artificial argn names which are generated by gtkdoc-scangobj.
4777 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4778 # FIXME: we need to check for empty docs here as well!
4780 # The templates contain the definitive parameter names and order,
4781 # so we will not change that. We only override the actual text.
4782 my $tmpl_params = $SymbolParams{$symbol};
4783 if (!defined ($tmpl_params)) {
4784 @TRACE@("No merge needed for $symbol\n");
4785 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4786 # FIXME: we still like to get the number of params and merge
4787 # 1) we would noticed that params have been removed/renamed
4788 # 2) we would catch undocumented params
4789 # params are not (yet) exported in -decl.txt so that we
4790 # could easily grab them :/
4792 my $params = $SourceSymbolParams{$symbol};
4794 @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4795 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4796 my $tmpl_param_name = $$tmpl_params[$j];
4798 # Try to find the param in the source comment documentation.
4801 @TRACE@(" try merge param $tmpl_param_name\n");
4802 for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4803 my $param_name = $$params[$k];
4804 my $param_desc = $$params[$k + 1];
4806 @TRACE@(" test param $param_name\n");
4807 # We accept changes in case, since the Gnome source
4808 # docs contain a lot of these.
4809 if ("\L$param_name" eq "\L$tmpl_param_name") {
4812 # Override the description.
4813 $$tmpl_params[$j + 1] = $param_desc;
4815 # Set the name to "" to mark it as used.
4821 # If it looks like the parameters are there, but not
4822 # in the right place, try to explain a bit better.
4823 if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4824 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4825 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4829 # Now we output a warning if parameters have been described which
4831 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4832 my $param_name = $$params[$j];
4834 # the template builder cannot detect if a macro returns
4836 if(($type eq "MACRO") && ($param_name eq "Returns")) {
4837 # FIXME: do we need to add it then to tmpl_params[] ?
4838 my $num=$#$tmpl_params;
4839 @TRACE@(" adding Returns: to macro docs for $symbol.\n");
4840 $$tmpl_params[$num+1]="Returns";
4841 $$tmpl_params[$num+2]=$$params[$j+1];
4844 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4845 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4851 if ($have_tmpl_docs) {
4852 $AllDocumentedSymbols{$symbol} = 1;
4853 @TRACE@("merging [$symbol] from template\n");
4856 @TRACE@("[$symbol] undocumented\n");
4860 # if this symbol is documented, check if docs are complete
4861 $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4862 # remove all xml-tags and whitespaces
4863 $check_tmpl_doc =~ s/<.*?>//g;
4864 $check_tmpl_doc =~ s/\s//g;
4865 if ($check_tmpl_doc ne "") {
4866 my $tmpl_params = $SymbolParams{$symbol};
4867 if (defined ($tmpl_params)) {
4868 my $type = $DeclarationTypes {$symbol};
4870 my $item = "Parameter";
4871 if (defined ($type)) {
4872 if ($type eq 'STRUCT') {
4874 } elsif ($type eq 'ENUM') {
4876 } elsif ($type eq 'UNION') {
4883 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4885 if ($#$tmpl_params > 0) {
4887 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4888 # Output a warning if the parameter is empty and
4889 # remember for stats.
4890 my $tmpl_param_name = $$tmpl_params[$j];
4891 my $tmpl_param_desc = $$tmpl_params[$j + 1];
4892 if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4893 if (exists ($AllIncompleteSymbols{$symbol})) {
4894 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4896 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4898 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4899 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4904 if ($#$tmpl_params == 0) {
4905 $AllIncompleteSymbols{$symbol}="<items>";
4906 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4907 "$item descriptions for $symbol are missing in source code comment block.");
4909 # $#$tmpl_params==-1 means we don't know about parameters
4910 # this unfortunately does not tell if there should be some
4915 @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4918 #############################################################################
4919 # Function : IsEmptyDoc
4920 # Description : Check if a doc-string is empty. Its also regarded as empty if
4921 # it only consist of whitespace or e.g. FIXME.
4922 # Arguments : the doc-string
4923 #############################################################################
4927 if ($doc =~ /^\s*$/) {
4931 if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4938 #############################################################################
4939 # Function : ConvertMarkDown
4940 # Description : Converts mark down syntax to the respective docbook.
4941 # http://de.wikipedia.org/wiki/Markdown
4942 # Inspired by the design of ParseDown
4943 # http://parsedown.org/
4944 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4945 # Arguments : the symbol name, the doc-string
4946 #############################################################################
4948 sub ConvertMarkDown {
4949 my ($symbol, $text) = @_;
4951 $text = &MarkDownParse ($text, $symbol);
4956 # SUPPORTED MARKDOWN
4957 # ==================
4966 # Setext-style Headers
4967 # --------------------
4975 # Ordered (unnested) Lists
4976 # ------------------------
4980 # 1. item 2 with loooong
4985 # Note: we require a blank line above the list items
4988 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4990 sub MarkDownParseBlocks {
4991 my ($linesref, $symbol, $context) = @_;
4994 my $md_block = { type => "" };
4996 OUTER: foreach $line (@$linesref) {
4997 my $first_char = substr ($line, 0, 1);
4998 my $deindented_line;
5000 @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
5002 if ($md_block->{"type"} eq "markup") {
5003 if (!$md_block->{"closed"}) {
5004 if (index ($line, $md_block->{"start"}) != -1) {
5005 $md_block->{"depth"}++;
5007 if (index ($line, $md_block->{"end"}) != -1) {
5008 if ($md_block->{"depth"} > 0) {
5009 $md_block->{"depth"}--;
5011 @TRACE@("closing tag '$line'");
5012 $md_block->{"closed"} = 1;
5013 # TODO(ensonic): reparse inner text with MarkDownParseLines?
5016 $md_block->{"text"} .= "\n" . $line;
5017 @TRACE@("add to markup");
5022 $deindented_line = $line;
5023 $deindented_line =~ s/^\s+//;
5025 if ($md_block->{"type"} eq "heading") {
5026 # a heading is ended by any level less than or equal
5027 if ($md_block->{"level"} == 1) {
5028 if ($line =~ /^={4,}[ \t]*$/) {
5029 my $text = pop @{$md_block->{"lines"}};
5030 $md_block->{"interrupted"} = 0;
5031 push @md_blocks, $md_block;
5033 $md_block = { type => "heading",
5038 } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5039 $md_block->{"interrupted"} = 0;
5040 push @md_blocks, $md_block;
5042 $md_block = { type => "heading",
5049 # push lines into the block until the end is reached
5050 push @{$md_block->{"lines"}}, $line;
5054 if ($line =~ /^[=]{4,}[ \t]*$/) {
5055 my $text = pop @{$md_block->{"lines"}};
5056 $md_block->{"interrupted"} = 0;
5057 push @md_blocks, $md_block;
5059 $md_block = { type => "heading",
5064 } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
5065 my $text = pop @{$md_block->{"lines"}};
5066 $md_block->{"interrupted"} = 0;
5067 push @md_blocks, $md_block;
5069 $md_block = { type => "heading",
5074 } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5075 $md_block->{"interrupted"} = 0;
5076 push @md_blocks, $md_block;
5078 $md_block = { type => "heading",
5082 level => length($1) };
5085 # push lines into the block until the end is reached
5086 push @{$md_block->{"lines"}}, $line;
5090 } elsif ($md_block->{"type"} eq "code") {
5091 if ($line =~ /^[ \t]*\]\|(.*)/) {
5092 push @md_blocks, $md_block;
5093 $md_block = { type => "paragraph",
5097 push @{$md_block->{"lines"}}, $line;
5102 if ($deindented_line eq "") {
5103 $md_block->{"interrupted"} = 1;
5107 if ($md_block->{"type"} eq "quote") {
5108 if (!$md_block->{"interrupted"}) {
5109 $line =~ s/^[ ]*>[ ]?//;
5110 push @{$md_block->{"lines"}}, $line;
5113 } elsif ($md_block->{"type"} eq "li") {
5114 my $marker = $md_block->{"marker"};
5115 if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
5116 my $indentation = $1;
5117 if ($md_block->{"indentation"} ne $indentation) {
5118 push @{$md_block->{"lines"}}, $line;
5121 my $ordered = $md_block->{"ordered"};
5122 $lines =~ s/^[ ]{0,4}//;
5123 $md_block->{"last"} = 0;
5124 push @md_blocks, $md_block;
5125 $md_block = { type => "li",
5126 ordered => $ordered,
5127 indentation => $indentation,
5131 lines => [ $lines ] };
5136 if ($md_block->{"interrupted"}) {
5137 if ($first_char eq " ") {
5138 push @{$md_block->{"lines"}}, "";
5139 $line =~ s/^[ ]{0,4}//;
5140 push @{$md_block->{"lines"}}, $line;
5141 $md_block->{"interrupted"} = 0;
5145 $line =~ s/^[ ]{0,4}//;
5146 push @{$md_block->{"lines"}}, $line;
5151 # indentation sensitive types
5152 @TRACE@("parsing '$line'");
5154 if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5156 push @md_blocks, $md_block;
5158 $md_block = { type => "heading",
5162 level => length($1) };
5165 } elsif ($line =~ /^={4,}[ \t]*$/) {
5166 # setext heading (====)
5168 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
5169 push @md_blocks, $md_block;
5170 $md_block->{"type"} = "heading";
5171 $md_block->{"lines"} = [];
5172 $md_block->{"level"} = 1;
5176 } elsif ($line =~ /^-{4,}[ \t]*$/) {
5177 # setext heading (-----)
5179 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
5180 push @md_blocks, $md_block;
5181 $md_block->{"type"} = "heading";
5182 $md_block->{"lines"} = [];
5183 $md_block->{"level"} = 2;
5187 } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
5189 $md_block->{"interrupted"} = 1;
5190 push @md_blocks, $md_block;
5191 $md_block = { type => "code",
5197 # indentation insensitive types
5198 if ($line =~ /^[ ]*<!DOCTYPE/) {
5199 push @md_blocks, $md_block;
5201 $md_block = { type => "markup",
5202 text => $deindented_line,
5208 } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
5209 # markup, including <?xml version="1.0"?>
5211 my $is_self_closing = defined($2);
5213 # skip link markdown
5214 # TODO(ensonic): consider adding more uri schemes (ftp, ...)
5215 if ($tag =~ /^https?/) {
5216 @TRACE@("skipping link '$tag'");
5218 # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
5219 # instead of creation a markdown block.
5220 my $scanning_for_end_of_text_level_tag = (
5221 $md_block->{"type"} eq "paragraph" &&
5222 defined($md_block->{"start"}) &&
5223 !$md_block->{"closed"});
5224 @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
5225 if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
5226 push @md_blocks, $md_block;
5228 if ($is_self_closing) {
5229 @TRACE@("self-closing docbook '$tag'");
5230 $md_block = { type => "self-closing tag",
5231 text => $deindented_line };
5232 $is_self_closing = 0;
5236 @TRACE@("new markup '$tag'");
5237 $md_block = { type => "markup",
5238 text => $deindented_line,
5239 start => "<" . $tag . ">",
5240 end => "</" . $tag . ">",
5243 if ($deindented_line =~ /<\/$tag>/) {
5244 $md_block->{"closed"} = 1;
5248 if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
5249 @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
5250 # TODO(ensonic): handle nesting
5251 if (!$scanning_for_end_of_text_level_tag) {
5252 if ($deindented_line !~ /<\/$tag>/) {
5253 @TRACE@("new text level markup '$tag'");
5254 $md_block->{"start"} = "<" . $tag . ">";
5255 $md_block->{"end"} = "</" . $tag . ">";
5256 $md_block->{"closed"} = 0;
5257 @TRACE@("scanning for end of '$tag'");
5260 if ($deindented_line =~ /$md_block->{"end"}/) {
5261 $md_block->{"closed"} = 1;
5262 @TRACE@("found end of '$tag'");
5268 } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
5270 push @md_blocks, $md_block;
5272 my $indentation = $1;
5273 $lines =~ s/^[ ]{0,4}//;
5274 $md_block = { type => "li",
5276 indentation => $indentation,
5280 lines => [ $lines ] };
5282 } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
5283 push @md_blocks, $md_block;
5284 $md_block = { type => "quote",
5290 if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5291 push @md_blocks, $md_block;
5293 my $indentation = $1;
5294 $lines =~ s/^[ ]{0,4}//;
5296 $md_block = { type => "li",
5298 indentation => $indentation,
5299 marker => "\\d+[.]",
5302 lines => [ $lines ] };
5308 if ($md_block->{"type"} eq "paragraph") {
5309 if ($md_block->{"interrupted"}) {
5310 push @md_blocks, $md_block;
5311 $md_block = { type => "paragraph",
5314 @TRACE@("new paragraph due to interrupted");
5316 $md_block->{"text"} .= "\n" . $line;
5317 @TRACE@("add to paragraph");
5320 push @md_blocks, $md_block;
5321 $md_block = { type => "paragraph",
5323 @TRACE@("new paragraph due to different block type");
5327 push @md_blocks, $md_block;
5334 sub MarkDownParseSpanElementsInner {
5335 my ($text, $markersref) = @_;
5337 my %markers = map { $_ => 1 } @$markersref;
5339 while ($text ne "") {
5340 my $closest_marker = "";
5341 my $closest_marker_index = 0;
5342 my $closest_marker_position = -1;
5343 my $text_marker = "";
5350 while ( ($marker, $use) = each %markers ) {
5351 my $marker_position;
5357 $marker_position = index ($text, $marker);
5359 if ($marker_position < 0) {
5360 $markers{$marker} = 0;
5364 if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5365 $closest_marker = $marker;
5366 $closest_marker_index = $i;
5367 $closest_marker_position = $marker_position;
5371 if ($closest_marker_position >= 0) {
5372 $text_marker = substr ($text, $closest_marker_position);
5375 if ($text_marker eq "") {
5381 $markup .= substr ($text, 0, $closest_marker_position);
5382 $text = substr ($text, $closest_marker_position);
5383 @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5385 if ($closest_marker eq "![" || $closest_marker eq "[") {
5388 if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5391 %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5394 $offset = length ($&);
5395 if ($element{"!"}) {
5399 $remaining_text = substr ($text, $offset);
5400 if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5405 $offset += length ($&);
5406 } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5407 $element{"ref"} = $1;
5408 $offset += length ($&);
5415 if ($element{"»"}) {
5416 $element{"»"} =~ s/&/&/g;
5417 $element{"»"} =~ s/</</g;
5419 if ($element{"!"}) {
5420 $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5422 if (defined ($element{"a"})) {
5423 $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5426 $markup .= "</inlinemediaobject>";
5427 } elsif ($element{"ref"}) {
5428 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5429 $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5431 if (defined ($element{"#"})) {
5432 # title attribute not supported
5435 $markup .= ">" . $element{"a"} . "</link>";
5437 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5438 $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5440 if (defined ($element{"#"})) {
5441 # title attribute not supported
5444 $markup .= ">" . $element{"a"} . "</ulink>";
5447 $markup .= $closest_marker;
5448 if ($closest_marker eq "![") {
5454 } elsif ($closest_marker eq "<") {
5455 if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5456 my $element_url = $1;
5457 $element_url =~ s/&/&/g;
5458 $element_url =~ s/</</g;
5460 $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5461 $offset = length ($&);
5462 } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5463 $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5464 $offset = length ($&);
5465 } elsif ($text =~ /^<[^>]+?>/) {
5467 $offset = length ($&);
5472 } elsif ($closest_marker eq "\\") {
5473 my $special_char = substr ($text, 1, 1);
5474 if ($MD_ESCAPABLE_CHARS{$special_char} ||
5475 $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5476 $markup .= $special_char;
5482 } elsif ($closest_marker eq "`") {
5483 if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5484 my $element_text = $2;
5485 $markup .= "<literal>" . $element_text . "</literal>";
5486 $offset = length ($&);
5491 } elsif ($closest_marker eq "@") {
5492 # Convert '@param()'
5493 # FIXME: we could make those also links ($symbol.$2), but that would be less
5494 # useful as the link target is a few lines up or down
5495 if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5496 $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5497 $offset = length ($&);
5498 } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5499 # Convert '@param', but not '\@param'.
5500 $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5501 $offset = length ($&);
5502 } elsif ($text =~ /^\\\@/) {
5504 $offset = length ($&);
5509 } elsif ($closest_marker eq "#") {
5510 if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5511 # handle #Object.func()
5512 $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5513 $offset = length ($&);
5514 } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5515 # Convert '#symbol', but not '\#symbol'.
5516 $markup .= $1 . &MakeHashXRef ($2, "type");
5517 $offset = length ($&);
5518 } elsif ($text =~ /^\\#/) {
5520 $offset = length ($&);
5525 } elsif ($closest_marker eq "%") {
5526 if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5527 # Convert '%constant', but not '\%constant'.
5528 # Also allow negative numbers, e.g. %-1.
5529 $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5530 $offset = length ($&);
5531 } elsif ($text =~ /^\\%/) {
5533 $offset = length ($&);
5541 $text = substr ($text, $offset);
5548 sub MarkDownParseSpanElements {
5550 my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5552 $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5554 # Convert 'function()' or 'macro()'.
5555 # if there is abc_*_def() we don't want to make a link to _def()
5556 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5557 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5562 sub ReplaceEntities {
5563 my ($text, $symbol) = @_;
5565 my @entities = ( [ "<", "<" ],
5574 [ "&", "&" ] ); # Do this last, or the others get messed up.
5577 # Expand entities in <programlisting> even inside CDATA since
5578 # we changed the definition of |[ to add CDATA
5579 for ($i = 0; $i <= $#entities; $i++) {
5580 $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5586 sub MarkDownOutputDocBook {
5587 my ($blocksref, $symbol, $context) = @_;
5590 my @blocks = @$blocksref;
5592 foreach $block (@blocks) {
5596 #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
5598 if ($block->{"type"} eq "paragraph") {
5599 $text = &MarkDownParseSpanElements ($block->{"text"});
5600 if ($context eq "li" && $output eq "") {
5601 if ($block->{"interrupted"}) {
5602 $output .= "\n<para>$text</para>\n";
5604 $output .= "<para>$text</para>";
5610 $output .= "<para>$text</para>\n";
5613 } elsif ($block->{"type"} eq "heading") {
5616 $title = &MarkDownParseSpanElements ($block->{"text"});
5618 if ($block->{"level"} == 1) {
5624 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5625 if (defined ($block->{"id"})) {
5626 $output .= "<$tag id=\"" . $block->{"id"} . "\">";
5628 $output .= "<$tag>";
5631 $output .= "<title>$title</title>$text</$tag>\n";
5632 } elsif ($block->{"type"} eq "li") {
5633 my $tag = "itemizedlist";
5635 if ($block->{"first"}) {
5636 if ($block->{"ordered"}) {
5637 $tag = "orderedlist";
5639 $output .= "<$tag>\n";
5642 if ($block->{"interrupted"}) {
5643 push @{$block->{"lines"}}, "";
5646 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5647 $output .= "<listitem>".$text."</listitem>\n";
5648 if ($block->{"last"}) {
5649 if ($block->{"ordered"}) {
5650 $tag = "orderedlist";
5652 $output .= "</$tag>\n";
5654 } elsif ($block->{"type"} eq "quote") {
5655 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5656 $output .= "<blockquote>\n$text</blockquote>\n";
5657 } elsif ($block->{"type"} eq "code") {
5658 my $tag = "programlisting";
5660 if ($block->{"language"}) {
5661 if ($block->{"language"} eq "plain") {
5662 $output .= "<informalexample><screen><![CDATA[\n";
5665 $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5668 $output .= "<informalexample><programlisting><![CDATA[\n";
5670 foreach (@{$block->{"lines"}}) {
5671 $output .= &ReplaceEntities ($_, $symbol) . "\n";
5673 $output .= "]]></$tag></informalexample>\n";
5674 } elsif ($block->{"type"} eq "markup") {
5675 $text = &ExpandAbbreviations($symbol, $block->{"text"});
5676 $output .= $text."\n";
5678 $output .= $block->{"text"}."\n";
5680 #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
5686 sub MarkDownParseLines {
5687 my ($linesref, $symbol, $context) = @_;
5689 my @lines = @$linesref;
5692 @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5693 $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5699 my ($text, $symbol) = @_;
5702 # take out some variability in line endings
5703 $text =~ s%\r\n%\n%g;
5707 @lines = split("\n", $text);
5708 $text = MarkDownParseLines(\@lines, $symbol, "");
5713 #############################################################################
5714 # LIBRARY FUNCTIONS - These functions are used in both gtkdoc-mkdb and
5715 # gtkdoc-mktmpl and should eventually be moved to a
5717 #############################################################################
5719 #############################################################################
5720 # Function : ReadDeclarationsFile
5721 # Description : This reads in a file containing the function/macro/enum etc.
5724 # Note that in some cases there are several declarations with
5725 # the same name, e.g. for conditional macros. In this case we
5726 # set a flag in the %DeclarationConditional hash so the
5727 # declaration is not shown in the docs.
5729 # If a macro and a function have the same name, e.g. for
5730 # gtk_object_ref, the function declaration takes precedence.
5732 # Some opaque structs are just declared with 'typedef struct
5733 # _name name;' in which case the declaration may be empty.
5734 # The structure may have been found later in the header, so
5735 # that overrides the empty declaration.
5737 # Arguments : $file - the declarations file to read
5738 # $override - if declarations in this file should override
5739 # any current declaration.
5740 #############################################################################
5742 sub ReadDeclarationsFile {
5743 my ($file, $override) = @_;
5745 if ($override == 0) {
5747 %DeclarationTypes = ();
5748 %DeclarationConditional = ();
5749 %DeclarationOutput = ();
5753 || die "Can't open $file: $!";
5754 my $declaration_type = "";
5755 my $declaration_name;
5757 my $is_deprecated = 0;
5759 if (!$declaration_type) {
5760 if (m/^<([^>]+)>/) {
5761 $declaration_type = $1;
5762 $declaration_name = "";
5763 @TRACE@("Found declaration: $declaration_type\n");
5767 if (m%^<NAME>(.*)</NAME>%) {
5768 $declaration_name = $1;
5769 } elsif (m%^<DEPRECATED/>%) {
5771 } elsif (m%^</$declaration_type>%) {
5772 @TRACE@("Found end of declaration: $declaration_name\n");
5773 # Check that the declaration has a name
5774 if ($declaration_name eq "") {
5775 &LogWarning ($file, $., "$declaration_type has no name.\n");
5778 # If the declaration is an empty typedef struct _XXX XXX
5779 # set the flag to indicate the struct has a typedef.
5780 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5781 && $declaration =~ m/^\s*$/) {
5782 @TRACE@("Struct has typedef: $declaration_name\n");
5783 $StructHasTypedef{$declaration_name} = 1;
5786 # Check if the symbol is already defined.
5787 if (defined ($Declarations{$declaration_name})
5788 && $override == 0) {
5789 # Function declarations take precedence.
5790 if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5792 } elsif ($declaration_type eq 'FUNCTION') {
5793 if ($is_deprecated) {
5794 $Deprecated{$declaration_name} = "";
5796 $Declarations{$declaration_name} = $declaration;
5797 $DeclarationTypes{$declaration_name} = $declaration_type;
5798 } elsif ($DeclarationTypes{$declaration_name}
5799 eq $declaration_type) {
5800 # If the existing declaration is empty, or is just a
5801 # forward declaration of a struct, override it.
5802 if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5803 if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5804 if ($is_deprecated) {
5805 $Deprecated{$declaration_name} = "";
5807 $Declarations{$declaration_name} = $declaration;
5808 } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5809 # Ignore an empty or forward declaration.
5811 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5814 # set flag in %DeclarationConditional hash for
5815 # multiply defined macros/typedefs.
5816 $DeclarationConditional{$declaration_name} = 1;
5819 &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5822 if ($is_deprecated) {
5823 $Deprecated{$declaration_name} = "";
5825 $Declarations{$declaration_name} = $declaration;
5826 $DeclarationTypes{$declaration_name} = $declaration_type;
5829 $declaration_type = "";
5840 #############################################################################
5841 # Function : ReadSignalsFile
5842 # Description : This reads in an existing file which contains information on
5843 # all GTK signals. It creates the arrays @SignalNames and
5844 # @SignalPrototypes containing info on the signals. The first
5845 # line of the SignalPrototype is the return type of the signal
5846 # handler. The remaining lines are the parameters passed to it.
5847 # The last parameter, "gpointer user_data" is always the same
5848 # so is not included.
5849 # Arguments : $file - the file containing the signal handler prototype
5851 #############################################################################
5853 sub ReadSignalsFile {
5861 my $signal_prototype;
5863 # Reset the signal info.
5864 @SignalObjects = ();
5866 @SignalReturns = ();
5868 @SignalPrototypes = ();
5873 if (!open (INPUT, $file)) {
5874 warn "Can't open $file - skipping signals\n";
5881 $signal_object = "";
5883 $signal_returns = "";
5884 $signal_prototype = "";
5887 if (m/^<NAME>(.*)<\/NAME>/) {
5889 if ($signal_name =~ m/^(.*)::(.*)$/) {
5890 $signal_object = $1;
5891 ($signal_name = $2) =~ s/_/-/g;
5892 @TRACE@("Found signal: $signal_name\n");
5894 &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5896 } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5897 $signal_returns = $1;
5898 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5900 } elsif (m%^</SIGNAL>%) {
5901 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5902 push (@SignalObjects, $signal_object);
5903 push (@SignalNames, $signal_name);
5904 push (@SignalReturns, $signal_returns);
5905 push (@SignalFlags, $signal_flags);
5906 push (@SignalPrototypes, $signal_prototype);
5909 $signal_prototype .= $_;
5917 #############################################################################
5918 # Function : ReadTemplateFile
5919 # Description : This reads in the manually-edited documentation file
5920 # corresponding to the file currently being created, so we can
5921 # insert the documentation at the appropriate places.
5922 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5923 # is a hash of arrays.
5924 # NOTE: This function is duplicated in gtkdoc-mktmpl (but
5925 # slightly different).
5926 # Arguments : $docsfile - the template file to read in.
5927 # $skip_unused_params - 1 if the unused parameters should be
5929 #############################################################################
5931 sub ReadTemplateFile {
5932 my ($docsfile, $skip_unused_params) = @_;
5934 my $template = "$docsfile.sgml";
5935 if (! -f $template) {
5936 @TRACE@("File doesn't exist: $template\n");
5940 # start with empty hashes, we merge the source comment for each file
5946 my $current_type = ""; # Type of symbol being read.
5947 my $current_symbol = ""; # Name of symbol being read.
5948 my $symbol_doc = ""; # Description of symbol being read.
5949 my @params; # Parameter names and descriptions of current
5950 # function/macro/function typedef.
5951 my $current_param = -1; # Index of parameter currently being read.
5952 # Note that the param array contains pairs
5953 # of param name & description.
5954 my $in_unused_params = 0; # True if we are reading in the unused params.
5955 my $in_deprecated = 0;
5957 my $in_stability = 0;
5959 open (DOCS, "$template")
5960 || die "Can't open $template: $!";
5962 @TRACE@("reading template $template");
5965 if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5968 if ($symbol eq "Title"
5969 || $symbol eq "Short_Description"
5970 || $symbol eq "Long_Description"
5971 || $symbol eq "See_Also"
5972 || $symbol eq "Stability_Level"
5973 || $symbol eq "Include"
5974 || $symbol eq "Image") {
5976 $symbol = $docsfile . ":" . $symbol;
5979 @TRACE@("Found symbol: $symbol\n");
5980 # Remember file and line for the symbol
5981 $SymbolSourceFile{$symbol} = $template;
5982 $SymbolSourceLine{$symbol} = $.;
5984 # Store previous symbol, but remove any trailing blank lines.
5985 if ($current_symbol ne "") {
5986 $symbol_doc =~ s/\s+$//;
5987 $SymbolTypes{$current_symbol} = $current_type;
5988 $SymbolDocs{$current_symbol} = $symbol_doc;
5990 # Check that the stability level is valid.
5991 if ($StabilityLevel{$current_symbol}) {
5992 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5995 if ($current_param >= 0) {
5996 $SymbolParams{$current_symbol} = [ @params ];
5998 # Delete any existing params in case we are overriding a
5999 # previously read template.
6000 delete $SymbolParams{$current_symbol};
6003 $current_type = $type;
6004 $current_symbol = $symbol;
6005 $current_param = -1;
6006 $in_unused_params = 0;
6013 } elsif (m/^<!-- # Unused Parameters # -->/) {
6014 @TRACE@("Found unused parameters\n");
6015 $in_unused_params = 1;
6018 } elsif ($in_unused_params && $skip_unused_params) {
6019 # When outputting the DocBook we skip unused parameters.
6020 @TRACE@("Skipping unused param: $_");
6024 # Check if param found. Need to handle "..." and "format...".
6025 if (s/^\@([\w\.]+):\040?//) {
6026 my $param_name = $1;
6027 my $param_desc = $_;
6028 # Allow variations of 'Returns'
6029 if ($param_name =~ m/^[Rr]eturns?$/) {
6030 $param_name = "Returns";
6032 # Allow varargs variations
6033 if ($param_name =~ m/^.*\.\.\.$/) {
6034 $param_name = "...";
6037 # strip trailing whitespaces and blank lines
6040 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
6042 if ($param_name eq "Deprecated") {
6044 $Deprecated{$current_symbol} = $_;
6045 } elsif ($param_name eq "Since") {
6048 $Since{$current_symbol} = $_;
6049 } elsif ($param_name eq "Stability") {
6051 $StabilityLevel{$current_symbol} = $_;
6053 push (@params, $param_name);
6054 push (@params, $param_desc);
6055 $current_param += $PARAM_FIELD_COUNT;
6058 # strip trailing whitespaces and blank lines
6063 if ($in_deprecated) {
6064 $Deprecated{$current_symbol} .= $_;
6065 } elsif ($in_since) {
6066 &LogWarning ($template, $., "multi-line since docs found");
6067 #$Since{$current_symbol} .= $_;
6068 } elsif ($in_stability) {
6069 $StabilityLevel{$current_symbol} .= $_;
6070 } elsif ($current_param >= 0) {
6071 $params[$current_param] .= $_;
6080 # Remember to finish the current symbol doccs.
6081 if ($current_symbol ne "") {
6083 $symbol_doc =~ s/\s+$//;
6084 $SymbolTypes{$current_symbol} = $current_type;
6085 $SymbolDocs{$current_symbol} = $symbol_doc;
6087 # Check that the stability level is valid.
6088 if ($StabilityLevel{$current_symbol}) {
6089 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
6092 if ($current_param >= 0) {
6093 $SymbolParams{$current_symbol} = [ @params ];
6095 # Delete any existing params in case we are overriding a
6096 # previously read template.
6097 delete $SymbolParams{$current_symbol};
6106 #############################################################################
6107 # Function : ReadObjectHierarchy
6108 # Description : This reads in the $MODULE-hierarchy.txt file containing all
6109 # the GtkObject subclasses described in this module (and their
6111 # It places them in the @Objects array, and places their level
6112 # in the object hierarchy in the @ObjectLevels array, at the
6113 # same index. GtkObject, the root object, has a level of 1.
6115 # This also generates tree_index.sgml as it goes along.
6118 #############################################################################
6120 sub ReadObjectHierarchy {
6124 if (! -f $OBJECT_TREE_FILE) {
6127 if (!open (INPUT, $OBJECT_TREE_FILE)) {
6128 warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
6132 # Only emit objects if they are supposed to be documented, or if
6133 # they have documented children. To implement this, we maintain a
6134 # stack of pending objects which will be emitted if a documented
6136 my @pending_objects = ();
6137 my @pending_levels = ();
6143 my $level = (length($`)) / 2 + 1;
6150 while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
6151 my $pobject = pop(@pending_objects);
6152 my $plevel = pop(@pending_levels);
6155 push (@pending_objects, $object);
6156 push (@pending_levels, $level);
6158 if (exists($KnownSymbols{$object})) {
6159 while ($#pending_levels >= 0) {
6160 $object = shift @pending_objects;
6161 $level = shift @pending_levels;
6162 $xref = &MakeXRef ($object);
6164 push (@tree, ' ' x ($level * 4) . "$xref");
6165 push (@Objects, $object);
6166 push (@ObjectLevels, $level);
6167 $ObjectRoots{$object} = $root;
6171 # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
6178 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
6179 my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
6180 my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
6182 open (OUTPUT, ">$new_tree_index")
6183 || die "Can't create $new_tree_index: $!";
6185 print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
6188 &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
6193 #############################################################################
6194 # Function : ReadInterfaces
6195 # Description : This reads in the $MODULE.interfaces file.
6198 #############################################################################
6200 sub ReadInterfaces {
6203 if (! -f $INTERFACES_FILE) {
6206 if (!open (INPUT, $INTERFACES_FILE)) {
6207 warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
6213 my ($object, @ifaces) = split;
6214 if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
6215 my @knownIfaces = ();
6217 # filter out private interfaces, but leave foreign interfaces
6218 foreach my $iface (@ifaces) {
6219 if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
6220 push (@knownIfaces, $iface);
6224 $Interfaces{$object} = join(' ', @knownIfaces);
6225 @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
6227 @TRACE@("skipping interfaces for unknown symbol: $object\n");
6233 #############################################################################
6234 # Function : ReadPrerequisites
6235 # Description : This reads in the $MODULE.prerequisites file.
6238 #############################################################################
6240 sub ReadPrerequisites {
6241 %Prerequisites = ();
6243 if (! -f $PREREQUISITES_FILE) {
6246 if (!open (INPUT, $PREREQUISITES_FILE)) {
6247 warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
6253 my ($iface, @prereqs) = split;
6254 if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
6255 my @knownPrereqs = ();
6257 # filter out private prerequisites, but leave foreign prerequisites
6258 foreach my $prereq (@prereqs) {
6259 if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
6260 push (@knownPrereqs, $prereq);
6264 $Prerequisites{$iface} = join(' ', @knownPrereqs);
6270 #############################################################################
6271 # Function : ReadArgsFile
6272 # Description : This reads in an existing file which contains information on
6273 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
6274 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
6276 # Arguments : $file - the file containing the arg information.
6277 #############################################################################
6292 # Reset the args info.
6305 if (!open (INPUT, $file)) {
6306 warn "Can't open $file - skipping args\n";
6323 if (m/^<NAME>(.*)<\/NAME>/) {
6325 if ($arg_name =~ m/^(.*)::(.*)$/) {
6327 ($arg_name = $2) =~ s/_/-/g;
6328 @TRACE@("Found arg: $arg_name\n");
6330 &LogWarning ($file, $., "Invalid argument name: $arg_name");
6332 } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6334 } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6336 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6338 } elsif (m/^<NICK>(.*)<\/NICK>/) {
6340 } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6342 if ($arg_blurb eq "(null)") {
6344 &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6346 } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6348 } elsif (m%^</ARG>%) {
6349 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6350 push (@ArgObjects, $arg_object);
6351 push (@ArgNames, $arg_name);
6352 push (@ArgTypes, $arg_type);
6353 push (@ArgRanges, $arg_range);
6354 push (@ArgFlags, $arg_flags);
6355 push (@ArgNicks, $arg_nick);
6356 push (@ArgBlurbs, $arg_blurb);
6357 push (@ArgDefaults, $arg_default);
6365 #############################################################################
6366 # Function : AddTreeLineArt
6367 # Description : Add unicode lineart to a pre-indented string array and returns
6368 # it as as multiline string.
6369 # Arguments : @tree - array of indented strings.
6370 #############################################################################
6372 sub AddTreeLineArt {
6373 my @tree = @{$_[0]};
6378 # iterate bottom up over the tree
6379 for ($i = $#tree; $i >= 0; $i--) {
6380 # count leading spaces
6381 $tree[$i] =~ /^([^<A-Za-z]*)/;
6382 $indent = length( $1 );
6383 # replace with ╰───, if place of ╰ is not space insert ├
6385 if (substr($tree[$i],$indent-4,1) eq " ") {
6386 substr($tree[$i],$indent-4,4) = "--- ";
6388 substr($tree[$i],$indent-4,4) = "+-- ";
6390 # go lines up while space and insert |
6391 for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6392 substr($tree[$j],$indent-4,1) = '|';
6397 my $res = join("\n", @tree);
6398 # unicode chars for: ╰──
6399 $res =~ s%---%<phrase role=\"lineart\">╰──</phrase>%g;
6400 # unicde chars for: ├──
6401 $res =~ s%\+--%<phrase role=\"lineart\">├──</phrase>%g;
6402 # unicode char for: │
6403 $res =~ s%\|%<phrase role=\"lineart\">│</phrase>%g;
6409 #############################################################################
6410 # Function : CheckIsObject
6411 # Description : Returns 1 if the given name is a GObject or a subclass.
6412 # It uses the global @Objects array.
6413 # Note that the @Objects array only contains classes in the
6414 # current module and their ancestors - not all GObject classes.
6415 # Arguments : $name - the name to check.
6416 #############################################################################
6420 my $root = $ObjectRoots{$name};
6421 # Let GBoxed pass as an object here to get -struct appended to the id
6422 # and prevent conflicts with sections.
6423 return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6427 #############################################################################
6428 # Function : MakeReturnField
6429 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6430 # Arguments : $str - the string to pad.
6431 #############################################################################
6433 sub MakeReturnField {
6436 return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6439 #############################################################################
6440 # Function : GetSymbolSourceFile
6441 # Description : Get the filename where the symbol docs where taken from.
6442 # Arguments : $symbol - the symbol name
6443 #############################################################################
6445 sub GetSymbolSourceFile {
6448 if (defined($SourceSymbolSourceFile{$symbol})) {
6449 return $SourceSymbolSourceFile{$symbol};
6450 } elsif (defined($SymbolSourceFile{$symbol})) {
6451 return $SymbolSourceFile{$symbol};
6457 #############################################################################
6458 # Function : GetSymbolSourceLine
6459 # Description : Get the file line where the symbol docs where taken from.
6460 # Arguments : $symbol - the symbol name
6461 #############################################################################
6463 sub GetSymbolSourceLine {
6466 if (defined($SourceSymbolSourceLine{$symbol})) {
6467 return $SourceSymbolSourceLine{$symbol};
6468 } elsif (defined($SymbolSourceLine{$symbol})) {
6469 return $SymbolSourceLine{$symbol};