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");
3966 my $pad1 = " " x (24 - length ($name));
3968 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";
3969 my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3970 $arg_desc .= MakeIndexterms($symbol, $id);
3972 $arg_desc .= OutputSymbolExtraLinks($symbol);
3974 $arg_desc .= "<programlisting> “$name”$pad1 $type_output</programlisting>\n";
3975 $arg_desc .= $blurb;
3976 if (defined ($SymbolAnnotations{$symbol})) {
3977 my $param_desc = $SymbolAnnotations{$symbol};
3978 my $param_annotations = "";
3979 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3980 if ($param_annotations ne "") {
3981 $arg_desc .= "\n<para>$param_annotations</para>";
3984 $arg_desc .= &MakeDeprecationNote($symbol);
3986 if ($flags_string) {
3987 $arg_desc .= "<para>Flags: $flags_string</para>\n";
3990 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3992 if ($default ne "") {
3993 $arg_desc .= "<para>Default value: $default_output</para>\n";
3995 $arg_desc .= OutputSymbolTraits ($symbol);
3996 $arg_desc .= "</refsect2>\n";
3998 if ($flags =~ m/c/) {
3999 $child_synop .= $arg_synop;
4000 $child_desc .= $arg_desc;
4002 elsif ($flags =~ m/s/) {
4003 $style_synop .= $arg_synop;
4004 $style_desc .= $arg_desc;
4007 $synop .= $arg_synop;
4012 return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
4016 #############################################################################
4017 # Function : ReadSourceDocumentation
4018 # Description : This reads in the documentation embedded in comment blocks
4019 # in the source code (for Gnome).
4021 # Parameter descriptions override any in the template files.
4022 # Function descriptions are placed before any description from
4023 # the template files.
4025 # It recursively descends the source directory looking for .c
4026 # files and scans them looking for specially-formatted comment
4029 # Arguments : $source_dir - the directory to scan.
4030 #############m###############################################################
4032 sub ReadSourceDocumentation {
4033 my ($source_dir) = @_;
4034 my ($file, $dir, @suffix_list, $suffix);
4036 # prepend entries from @SOURCE_DIR
4037 for my $dir (@SOURCE_DIRS) {
4038 # Check if the filename is in the ignore list.
4039 if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
4040 @TRACE@("Skipping source directory: $source_dir");
4043 @TRACE@("No match for: ".($1 || $source_dir));
4047 @TRACE@("Scanning source directory: $source_dir");
4049 # This array holds any subdirectories found.
4052 @suffix_list = split (/,/, $SOURCE_SUFFIXES);
4054 opendir (SRCDIR, $source_dir)
4055 || die "Can't open source directory $source_dir: $!";
4057 foreach $file (readdir (SRCDIR)) {
4058 if ($file =~ /^\./) {
4060 } elsif (-d "$source_dir/$file") {
4061 push (@subdirs, $file);
4062 } elsif (@suffix_list) {
4063 foreach $suffix (@suffix_list) {
4064 if ($file =~ m/\.\Q${suffix}\E$/) {
4065 &ScanSourceFile ("$source_dir/$file");
4068 } elsif ($file =~ m/\.[ch]$/) {
4069 &ScanSourceFile ("$source_dir/$file");
4074 # Now recursively scan the subdirectories.
4075 foreach $dir (@subdirs) {
4076 &ReadSourceDocumentation ("$source_dir/$dir");
4081 #############################################################################
4082 # Function : ScanSourceFile
4083 # Description : Scans one source file looking for specially-formatted comment
4084 # blocks. Later &MergeSourceDocumentation is used to merge any
4085 # documentation found with the documentation already read in
4086 # from the template files.
4088 # Arguments : $file - the file to scan.
4089 #############################################################################
4091 sub ScanSourceFile {
4095 # prepend entries from @SOURCE_DIR
4096 for my $dir (@SOURCE_DIRS) {
4097 # Check if the filename is in the ignore list.
4098 if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
4099 @TRACE@("Skipping source file: $file");
4104 if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
4107 &LogWarning ($file, 1, "Can't find basename for this filename.");
4111 # Check if the basename is in the list of files to ignore.
4112 if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
4113 @TRACE@("Skipping source file: $file");
4117 @TRACE@("Scanning source file: $file");
4119 open (SRCFILE, $file)
4120 || die "Can't open $file: $!";
4121 my $in_comment_block = 0;
4124 my ($description, $return_desc);
4125 my ($since_desc, $stability_desc, $deprecated_desc);
4129 # Look for the start of a comment block.
4130 if (!$in_comment_block) {
4131 if (m%^\s*/\*.*\*/%) {
4132 #one-line comment - not gtkdoc
4133 } elsif (m%^\s*/\*\*\s%) {
4134 @TRACE@("Found comment block start\n");
4136 $in_comment_block = 1;
4138 # Reset all the symbol data.
4144 $deprecated_desc = "";
4145 $stability_desc = "";
4146 $current_param = -1;
4152 # We're in a comment block. Check if we've found the end of it.
4155 # maybe its not even meant to be a gtk-doc comment?
4156 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
4158 # Add the return value description onto the end of the params.
4160 # TODO(ensonic): check for duplicated Return docs
4161 # &LogWarning ($file, $., "Multiple Returns for $symbol.");
4162 push (@params, "Returns");
4163 push (@params, $return_desc);
4165 # Convert special characters
4166 $description = &ConvertSGMLChars ($symbol, $description);
4168 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4169 $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
4172 # Handle Section docs
4173 if ($symbol =~ m/SECTION:\s*(.*)/) {
4177 if (scalar %KnownSymbols) {
4178 if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
4179 &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
4183 @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
4184 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4185 @TRACE@(" '".$params[$k]."'\n");
4186 $params[$k] = "\L$params[$k]";
4188 if ($params[$k] eq "short_description") {
4189 $key = "$TMPL_DIR/$real_symbol:Short_Description";
4190 } elsif ($params[$k] eq "see_also") {
4191 $key = "$TMPL_DIR/$real_symbol:See_Also";
4192 } elsif ($params[$k] eq "title") {
4193 $key = "$TMPL_DIR/$real_symbol:Title";
4194 } elsif ($params[$k] eq "stability") {
4195 $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4196 } elsif ($params[$k] eq "section_id") {
4197 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4198 } elsif ($params[$k] eq "include") {
4199 $key = "$TMPL_DIR/$real_symbol:Include";
4200 } elsif ($params[$k] eq "image") {
4201 $key = "$TMPL_DIR/$real_symbol:Image";
4203 if (defined($key)) {
4204 $SourceSymbolDocs{$key}=$params[$k+1];
4205 $SourceSymbolSourceFile{$key} = $file;
4206 $SourceSymbolSourceLine{$key} = $.;
4209 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4210 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4211 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4212 #$SourceSymbolTypes{$symbol} = "SECTION";
4213 } elsif ($symbol =~ m/PROGRAM:\s*(.*)/) {
4214 my $real_symbol = $1;
4218 @TRACE@("PROGRAM DOCS found in source for '$real_symbol'\n");
4219 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4220 @TRACE@(" '".$params[$k]."'\n");
4221 $params[$k] = "\L$params[$k]";
4224 if ($params[$k] eq "short_description") {
4225 $key = "$TMPL_DIR/$real_symbol:Short_Description";
4226 } elsif ($params[$k] eq "see_also") {
4227 $key = "$TMPL_DIR/$real_symbol:See_Also";
4228 } elsif ($params[$k] eq "section_id") {
4229 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4230 } elsif ($params[$k] eq "synopsis") {
4231 $key = "$TMPL_DIR/$real_symbol:Synopsis";
4232 } elsif ($params[$k] eq "returns") {
4233 $key = "$TMPL_DIR/$real_symbol:Returns";
4234 } elsif ($params[$k] =~ m/^(-.*)/) {
4235 $key = "$TMPL_DIR/$real_symbol:Options";
4237 if (defined($SourceSymbolDocs{$key})) {
4238 $opts = $SourceSymbolDocs{$key};
4242 push (@{ $opts }, $1);
4243 push (@{ $opts }, $params[$k+1]);
4245 $SourceSymbolDocs{$key} = $opts;
4248 if (defined($key)) {
4249 $SourceSymbolDocs{$key}=$params[$k+1];
4250 $SourceSymbolSourceFile{$key} = $file;
4251 $SourceSymbolSourceLine{$key} = $.;
4254 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4255 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4256 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4258 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Section_Id"};
4259 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
4260 # Remove trailing blanks and use as is
4261 $section_id =~ s/\s+$//;
4263 $section_id = &CreateValidSGMLID ("$MODULE-$real_symbol");
4266 &OutputProgramDBFile ($real_symbol, $section_id);
4269 @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4270 $SourceSymbolDocs{$symbol} = $description;
4271 $SourceSymbolParams{$symbol} = [ @params ];
4272 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4273 #if (defined $DeclarationTypes{$symbol}) {
4274 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4276 $SourceSymbolSourceFile{$symbol} = $file;
4277 $SourceSymbolSourceLine{$symbol} = $.;
4281 ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4282 $since_desc =~ s/^\s+//;
4283 $since_desc =~ s/\s+$//;
4284 @TRACE@("Since($symbol) : [$since_desc]\n");
4285 $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4286 if(scalar @extra_lines) {
4287 &LogWarning ($file, $., "multi-line since docs found");
4291 if ($stability_desc) {
4292 $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4293 $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4296 if ($deprecated_desc) {
4297 if (!exists $Deprecated{$symbol}) {
4298 # don't warn for signals and properties
4299 #if ($symbol !~ m/::?(.*)/) {
4300 if (defined $DeclarationTypes{$symbol}) {
4301 &LogWarning ($file, $.,
4302 "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4303 " (See the --deprecated-guards option for gtkdoc-scan.)");
4306 $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4310 $in_comment_block = 0;
4314 # Get rid of ' * ' at start of every line in the comment block.
4316 # But make sure we don't get rid of the newline at the end.
4320 @TRACE@("scanning :$_");
4322 # If we haven't found the symbol name yet, look for it.
4324 if (m%^\s*(SECTION:\s*\S+)%) {
4326 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4327 } elsif (m%^\s*(PROGRAM:\s*\S+)%) {
4329 @TRACE@("PROGRAM DOCS found in source for : '$symbol'\n");
4330 } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._() ]+?\)\s*)*$%) {
4332 my $annotation = $2;
4333 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4334 if (defined($annotation)) {
4336 if ($annotation ne "") {
4337 $SymbolAnnotations{$symbol} = $annotation;
4338 @TRACE@("remaining text for $symbol: '$annotation'\n");
4345 if ($in_part eq "description") {
4346 # Get rid of 'Description:'
4347 s%^\s*Description:%%;
4350 if (m%^\s*(returns|return\s+value):%i) {
4351 # we're in param section and have not seen the blank line
4352 if($in_part ne "") {
4354 $in_part = "return";
4357 } elsif (m%^\s*since:%i) {
4358 # we're in param section and have not seen the blank line
4359 if($in_part ne "param") {
4364 } elsif (m%^\s*deprecated:%i) {
4365 # we're in param section and have not seen the blank line
4366 if($in_part ne "param") {
4367 $deprecated_desc = $';
4368 $in_part = "deprecated";
4371 } elsif (m%^\s*stability:%i) {
4372 $stability_desc = $';
4373 $in_part = "stability";
4377 if ($in_part eq "description") {
4380 } elsif ($in_part eq "return") {
4383 } elsif ($in_part eq "since") {
4386 } elsif ($in_part eq "stability") {
4387 $stability_desc .= $_;
4389 } elsif ($in_part eq "deprecated") {
4390 $deprecated_desc .= $_;
4394 # We must be in the parameters. Check for the empty line below them.
4396 $in_part = "description";
4400 # Look for a parameter name.
4401 if (m%^\s*@(.+?)\s*:\s*%) {
4402 my $param_name = $1;
4403 my $param_desc = $';
4405 @TRACE@("Found parameter: $param_name\n");
4406 # Allow varargs variations
4407 if ($param_name =~ m/^\.\.\.$/) {
4408 $param_name = "...";
4410 @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4412 push (@params, $param_name);
4413 push (@params, $param_desc);
4414 $current_param += $PARAM_FIELD_COUNT;
4417 } elsif ($in_part eq "") {
4418 @TRACE@("continuation for $symbol annotation '$_'");
4419 my $annotation = $_;
4420 $annotation =~ s/^\s+|\s+$//g ;
4421 $SymbolAnnotations{$symbol} .= $annotation;
4425 # We must be in the middle of a parameter description, so add it on
4426 # to the last element in @params.
4427 if ($current_param == -1) {
4428 &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
4430 $params[$#params] .= $_;
4436 #############################################################################
4437 # Function : OutputMissingDocumentation
4438 # Description : Outputs report of documentation coverage to a file
4441 #############################################################################
4443 sub OutputMissingDocumentation {
4444 my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4445 my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4447 my $n_documented = 0;
4448 my $n_incomplete = 0;
4454 my $buffer_deprecated = "";
4455 my $buffer_descriptions = "";
4457 open(UNDOCUMENTED, ">$new_undocumented_file")
4458 || die "Can't create $new_undocumented_file";
4460 foreach $symbol (sort (keys (%AllSymbols))) {
4461 # FIXME: should we print LogWarnings for undocumented stuff?
4463 #my $ssfile = &GetSymbolSourceFile($symbol);
4464 #my $ssline = &GetSymbolSourceLine($symbol);
4465 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4467 if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4469 if (exists ($AllDocumentedSymbols{$symbol})) {
4471 if (exists ($AllIncompleteSymbols{$symbol})) {
4473 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4474 #$buffer .= "\t0: ".$location;
4476 } elsif (exists $Deprecated{$symbol}) {
4477 if (exists ($AllIncompleteSymbols{$symbol})) {
4479 $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4480 #$buffer .= "\t1a: ".$location;
4482 $buffer_deprecated .= $symbol . "\n";
4483 #$buffer .= "\t1b: ".$location;
4486 if (exists ($AllIncompleteSymbols{$symbol})) {
4488 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4489 #$buffer .= "\t2a: ".$location;
4491 $buffer .= $symbol . "\n";
4492 #$buffer .= "\t2b: ".$location;
4495 } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4497 if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4498 || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4501 # cut off the leading namespace ($TMPL_DIR)
4502 $symbol =~ m/^.*\/(.*)$/;
4503 $buffer_descriptions .= $1 . "\n";
4511 $percent = ($n_documented / $total) * 100.0;
4514 printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4515 print UNDOCUMENTED "$n_documented symbols documented.\n";
4516 print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4517 print UNDOCUMENTED ($total - $n_documented) . " not documented.\n";
4519 if ($buffer_deprecated ne "") {
4520 $buffer .= "\n" . $buffer_deprecated;
4522 if ($buffer_descriptions ne "") {
4523 $buffer .= "\n" . $buffer_descriptions;
4525 if ($buffer ne "") {
4526 print UNDOCUMENTED "\n\n$buffer";
4528 close (UNDOCUMENTED);
4530 return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4532 printf "%.0f%% symbol docs coverage", $percent;
4533 print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4534 print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4538 #############################################################################
4539 # Function : OutputUndeclaredSymbols
4540 # Description : Outputs symbols that are listed in the section file, but not
4541 # declaration is found in the sources
4544 #############################################################################
4546 sub OutputUndeclaredSymbols {
4547 my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4548 my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4550 open(UNDECLARED, ">$new_undeclared_file")
4551 || die "Can't create $new_undeclared_file";
4553 if (%UndeclaredSymbols) {
4554 print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4555 print UNDECLARED "\n";
4556 print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4560 return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4563 #############################################################################
4564 # Function : OutputUnusedSymbols
4565 # Description : Outputs symbols that are documented in comments, but not
4566 # declared in the sources
4569 #############################################################################
4571 sub OutputUnusedSymbols {
4573 my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4574 my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4576 open (UNUSED, ">$new_unused_file")
4577 || die "Can't open $new_unused_file";
4579 foreach $symbol (sort keys (%Declarations)) {
4580 if (!defined ($DeclarationOutput{$symbol})) {
4581 print (UNUSED "$symbol\n");
4585 foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4586 print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4590 if ($num_unused != 0) {
4591 &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4592 "They should be added to $MODULE-sections.txt in the appropriate place.");
4595 return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4599 #############################################################################
4600 # Function : OutputAllSymbols
4601 # Description : Outputs list of all symbols to a file
4604 #############################################################################
4606 sub OutputAllSymbols {
4607 my $n_documented = 0;
4613 open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4614 || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4616 foreach $symbol (sort (keys (%AllSymbols))) {
4617 print SYMBOLS $symbol . "\n";
4623 #############################################################################
4624 # Function : OutputSymbolsWithoutSince
4625 # Description : Outputs list of all symbols without a since tag to a file
4628 #############################################################################
4630 sub OutputSymbolsWithoutSince {
4631 my $n_documented = 0;
4637 open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4638 || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4640 foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4641 if (!defined $Since{$symbol}) {
4642 print SYMBOLS $symbol . "\n";
4650 #############################################################################
4651 # Function : MergeSourceDocumentation
4652 # Description : This merges documentation read from a source file into the
4653 # documentation read in from a template file.
4655 # Parameter descriptions override any in the template files.
4656 # Function descriptions are placed before any description from
4657 # the template files.
4660 #############################################################################
4662 sub MergeSourceDocumentation {
4666 if (scalar %SymbolDocs) {
4667 @Symbols=keys (%SymbolDocs);
4668 @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4671 # filter scanned declarations, with what we suppress from -sections.txt
4673 foreach $symbol (keys (%Declarations)) {
4674 if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4678 # , add the rest from -sections.txt
4679 foreach $symbol (keys (%KnownSymbols)) {
4680 if ($KnownSymbols{$symbol} == 1) {
4684 # and add whats found in the source
4685 foreach $symbol (keys (%SourceSymbolDocs)) {
4688 @Symbols = keys (%tmp);
4689 @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4691 foreach $symbol (@Symbols) {
4692 $AllSymbols{$symbol} = 1;
4694 my $have_tmpl_docs = 0;
4696 ## see if the symbol is documented in template
4697 my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4698 my $check_tmpl_doc =$tmpl_doc;
4699 # remove all xml-tags and whitespaces
4700 $check_tmpl_doc =~ s/<.*?>//g;
4701 $check_tmpl_doc =~ s/\s//g;
4703 if ($check_tmpl_doc ne "") {
4704 $have_tmpl_docs = 1;
4706 # if the docs have just an empty para, don't merge that.
4707 $check_tmpl_doc = $tmpl_doc;
4708 $check_tmpl_doc =~ s/(\s|\n)//msg;
4709 if ($check_tmpl_doc eq "<para></para>") {
4714 if (exists ($SourceSymbolDocs{$symbol})) {
4715 my $type = $DeclarationTypes {$symbol};
4717 @TRACE@("merging [$symbol] from source\n");
4719 my $item = "Parameter";
4720 if (defined ($type)) {
4721 if ($type eq 'STRUCT') {
4723 } elsif ($type eq 'ENUM') {
4725 } elsif ($type eq 'UNION') {
4732 my $src_doc = $SourceSymbolDocs{$symbol};
4733 # remove leading and training whitespaces
4734 $src_doc =~ s/^\s+//;
4735 $src_doc =~ s/\s+$//;
4737 # Don't output warnings for overridden titles as titles are
4738 # automatically generated in the -sections.txt file, and thus they
4739 # are often overridden.
4740 if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4741 # check if content is different
4742 if ($tmpl_doc ne $src_doc) {
4743 #print "[$tmpl_doc] [$src_doc]\n";
4744 &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4745 "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4749 if ($src_doc ne "") {
4750 $AllDocumentedSymbols{$symbol} = 1;
4753 # Do not add <para> to nothing, it breaks missing docs checks.
4754 my $src_doc_para = "";
4755 if ($src_doc ne "") {
4756 $src_doc_para = $src_doc;
4759 if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4760 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4761 } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4762 # For the title/summary/see also section docs we don't want to
4763 # add any <para> tags.
4764 $SymbolDocs{$symbol} = "$src_doc"
4766 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4770 if ($symbol =~ m/.*::.*/) {
4771 # For signals we prefer the param names from the source docs,
4772 # since the ones from the templates are likely to contain the
4773 # artificial argn names which are generated by gtkdoc-scangobj.
4774 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4775 # FIXME: we need to check for empty docs here as well!
4777 # The templates contain the definitive parameter names and order,
4778 # so we will not change that. We only override the actual text.
4779 my $tmpl_params = $SymbolParams{$symbol};
4780 if (!defined ($tmpl_params)) {
4781 @TRACE@("No merge needed for $symbol\n");
4782 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4783 # FIXME: we still like to get the number of params and merge
4784 # 1) we would noticed that params have been removed/renamed
4785 # 2) we would catch undocumented params
4786 # params are not (yet) exported in -decl.txt so that we
4787 # could easily grab them :/
4789 my $params = $SourceSymbolParams{$symbol};
4791 @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4792 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4793 my $tmpl_param_name = $$tmpl_params[$j];
4795 # Try to find the param in the source comment documentation.
4798 @TRACE@(" try merge param $tmpl_param_name\n");
4799 for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4800 my $param_name = $$params[$k];
4801 my $param_desc = $$params[$k + 1];
4803 @TRACE@(" test param $param_name\n");
4804 # We accept changes in case, since the Gnome source
4805 # docs contain a lot of these.
4806 if ("\L$param_name" eq "\L$tmpl_param_name") {
4809 # Override the description.
4810 $$tmpl_params[$j + 1] = $param_desc;
4812 # Set the name to "" to mark it as used.
4818 # If it looks like the parameters are there, but not
4819 # in the right place, try to explain a bit better.
4820 if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4821 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4822 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4826 # Now we output a warning if parameters have been described which
4828 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4829 my $param_name = $$params[$j];
4831 # the template builder cannot detect if a macro returns
4833 if(($type eq "MACRO") && ($param_name eq "Returns")) {
4834 # FIXME: do we need to add it then to tmpl_params[] ?
4835 my $num=$#$tmpl_params;
4836 @TRACE@(" adding Returns: to macro docs for $symbol.\n");
4837 $$tmpl_params[$num+1]="Returns";
4838 $$tmpl_params[$num+2]=$$params[$j+1];
4841 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4842 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4848 if ($have_tmpl_docs) {
4849 $AllDocumentedSymbols{$symbol} = 1;
4850 @TRACE@("merging [$symbol] from template\n");
4853 @TRACE@("[$symbol] undocumented\n");
4857 # if this symbol is documented, check if docs are complete
4858 $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4859 # remove all xml-tags and whitespaces
4860 $check_tmpl_doc =~ s/<.*?>//g;
4861 $check_tmpl_doc =~ s/\s//g;
4862 if ($check_tmpl_doc ne "") {
4863 my $tmpl_params = $SymbolParams{$symbol};
4864 if (defined ($tmpl_params)) {
4865 my $type = $DeclarationTypes {$symbol};
4867 my $item = "Parameter";
4868 if (defined ($type)) {
4869 if ($type eq 'STRUCT') {
4871 } elsif ($type eq 'ENUM') {
4873 } elsif ($type eq 'UNION') {
4880 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4882 if ($#$tmpl_params > 0) {
4884 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4885 # Output a warning if the parameter is empty and
4886 # remember for stats.
4887 my $tmpl_param_name = $$tmpl_params[$j];
4888 my $tmpl_param_desc = $$tmpl_params[$j + 1];
4889 if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4890 if (exists ($AllIncompleteSymbols{$symbol})) {
4891 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4893 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4895 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4896 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4901 if ($#$tmpl_params == 0) {
4902 $AllIncompleteSymbols{$symbol}="<items>";
4903 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4904 "$item descriptions for $symbol are missing in source code comment block.");
4906 # $#$tmpl_params==-1 means we don't know about parameters
4907 # this unfortunately does not tell if there should be some
4912 @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4915 #############################################################################
4916 # Function : IsEmptyDoc
4917 # Description : Check if a doc-string is empty. Its also regarded as empty if
4918 # it only consist of whitespace or e.g. FIXME.
4919 # Arguments : the doc-string
4920 #############################################################################
4924 if ($doc =~ /^\s*$/) {
4928 if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4935 #############################################################################
4936 # Function : ConvertMarkDown
4937 # Description : Converts mark down syntax to the respective docbook.
4938 # http://de.wikipedia.org/wiki/Markdown
4939 # Inspired by the design of ParseDown
4940 # http://parsedown.org/
4941 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4942 # Arguments : the symbol name, the doc-string
4943 #############################################################################
4945 sub ConvertMarkDown {
4946 my ($symbol, $text) = @_;
4948 $text = &MarkDownParse ($text, $symbol);
4953 # SUPPORTED MARKDOWN
4954 # ==================
4963 # Setext-style Headers
4964 # --------------------
4972 # Ordered (unnested) Lists
4973 # ------------------------
4977 # 1. item 2 with loooong
4982 # Note: we require a blank line above the list items
4985 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4987 sub MarkDownParseBlocks {
4988 my ($linesref, $symbol, $context) = @_;
4991 my $md_block = { type => "" };
4993 OUTER: foreach $line (@$linesref) {
4994 my $first_char = substr ($line, 0, 1);
4995 my $deindented_line;
4997 @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
4999 if ($md_block->{"type"} eq "markup") {
5000 if (!$md_block->{"closed"}) {
5001 if (index ($line, $md_block->{"start"}) != -1) {
5002 $md_block->{"depth"}++;
5004 if (index ($line, $md_block->{"end"}) != -1) {
5005 if ($md_block->{"depth"} > 0) {
5006 $md_block->{"depth"}--;
5008 @TRACE@("closing tag '$line'");
5009 $md_block->{"closed"} = 1;
5010 # TODO(ensonic): reparse inner text with MarkDownParseLines?
5013 $md_block->{"text"} .= "\n" . $line;
5014 @TRACE@("add to markup");
5019 $deindented_line = $line;
5020 $deindented_line =~ s/^\s+//;
5022 if ($md_block->{"type"} eq "heading") {
5023 # a heading is ended by any level less than or equal
5024 if ($md_block->{"level"} == 1) {
5025 if ($line =~ /^={4,}[ \t]*$/) {
5026 my $text = pop @{$md_block->{"lines"}};
5027 $md_block->{"interrupted"} = 0;
5028 push @md_blocks, $md_block;
5030 $md_block = { type => "heading",
5035 } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5036 $md_block->{"interrupted"} = 0;
5037 push @md_blocks, $md_block;
5039 $md_block = { type => "heading",
5046 # push lines into the block until the end is reached
5047 push @{$md_block->{"lines"}}, $line;
5051 if ($line =~ /^[=]{4,}[ \t]*$/) {
5052 my $text = pop @{$md_block->{"lines"}};
5053 $md_block->{"interrupted"} = 0;
5054 push @md_blocks, $md_block;
5056 $md_block = { type => "heading",
5061 } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
5062 my $text = pop @{$md_block->{"lines"}};
5063 $md_block->{"interrupted"} = 0;
5064 push @md_blocks, $md_block;
5066 $md_block = { type => "heading",
5071 } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5072 $md_block->{"interrupted"} = 0;
5073 push @md_blocks, $md_block;
5075 $md_block = { type => "heading",
5079 level => length($1) };
5082 # push lines into the block until the end is reached
5083 push @{$md_block->{"lines"}}, $line;
5087 } elsif ($md_block->{"type"} eq "code") {
5088 if ($line =~ /^[ \t]*\]\|(.*)/) {
5089 push @md_blocks, $md_block;
5090 $md_block = { type => "paragraph",
5094 push @{$md_block->{"lines"}}, $line;
5099 if ($deindented_line eq "") {
5100 $md_block->{"interrupted"} = 1;
5104 if ($md_block->{"type"} eq "quote") {
5105 if (!$md_block->{"interrupted"}) {
5106 $line =~ s/^[ ]*>[ ]?//;
5107 push @{$md_block->{"lines"}}, $line;
5110 } elsif ($md_block->{"type"} eq "li") {
5111 my $marker = $md_block->{"marker"};
5112 if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
5113 my $indentation = $1;
5114 if ($md_block->{"indentation"} ne $indentation) {
5115 push @{$md_block->{"lines"}}, $line;
5118 my $ordered = $md_block->{"ordered"};
5119 $lines =~ s/^[ ]{0,4}//;
5120 $md_block->{"last"} = 0;
5121 push @md_blocks, $md_block;
5122 $md_block = { type => "li",
5123 ordered => $ordered,
5124 indentation => $indentation,
5128 lines => [ $lines ] };
5133 if ($md_block->{"interrupted"}) {
5134 if ($first_char eq " ") {
5135 push @{$md_block->{"lines"}}, "";
5136 $line =~ s/^[ ]{0,4}//;
5137 push @{$md_block->{"lines"}}, $line;
5138 $md_block->{"interrupted"} = 0;
5142 $line =~ s/^[ ]{0,4}//;
5143 push @{$md_block->{"lines"}}, $line;
5148 # indentation sensitive types
5149 @TRACE@("parsing '$line'");
5151 if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5153 push @md_blocks, $md_block;
5155 $md_block = { type => "heading",
5159 level => length($1) };
5162 } elsif ($line =~ /^={4,}[ \t]*$/) {
5163 # setext heading (====)
5165 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
5166 push @md_blocks, $md_block;
5167 $md_block->{"type"} = "heading";
5168 $md_block->{"lines"} = [];
5169 $md_block->{"level"} = 1;
5173 } elsif ($line =~ /^-{4,}[ \t]*$/) {
5174 # setext heading (-----)
5176 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
5177 push @md_blocks, $md_block;
5178 $md_block->{"type"} = "heading";
5179 $md_block->{"lines"} = [];
5180 $md_block->{"level"} = 2;
5184 } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
5186 $md_block->{"interrupted"} = 1;
5187 push @md_blocks, $md_block;
5188 $md_block = { type => "code",
5194 # indentation insensitive types
5195 if ($line =~ /^[ ]*<!DOCTYPE/) {
5196 push @md_blocks, $md_block;
5198 $md_block = { type => "markup",
5199 text => $deindented_line,
5205 } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
5206 # markup, including <?xml version="1.0"?>
5208 my $is_self_closing = defined($2);
5210 # skip link markdown
5211 # TODO(ensonic): consider adding more uri schemes (ftp, ...)
5212 if ($tag =~ /^https?/) {
5213 @TRACE@("skipping link '$tag'");
5215 # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
5216 # instead of creation a markdown block.
5217 my $scanning_for_end_of_text_level_tag = (
5218 $md_block->{"type"} eq "paragraph" &&
5219 defined($md_block->{"start"}) &&
5220 !$md_block->{"closed"});
5221 @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
5222 if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
5223 push @md_blocks, $md_block;
5225 if ($is_self_closing) {
5226 @TRACE@("self-closing docbook '$tag'");
5227 $md_block = { type => "self-closing tag",
5228 text => $deindented_line };
5229 $is_self_closing = 0;
5233 @TRACE@("new markup '$tag'");
5234 $md_block = { type => "markup",
5235 text => $deindented_line,
5236 start => "<" . $tag . ">",
5237 end => "</" . $tag . ">",
5240 if ($deindented_line =~ /<\/$tag>/) {
5241 $md_block->{"closed"} = 1;
5245 if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
5246 @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
5247 # TODO(ensonic): handle nesting
5248 if (!$scanning_for_end_of_text_level_tag) {
5249 if ($deindented_line !~ /<\/$tag>/) {
5250 @TRACE@("new text level markup '$tag'");
5251 $md_block->{"start"} = "<" . $tag . ">";
5252 $md_block->{"end"} = "</" . $tag . ">";
5253 $md_block->{"closed"} = 0;
5254 @TRACE@("scanning for end of '$tag'");
5257 if ($deindented_line =~ /$md_block->{"end"}/) {
5258 $md_block->{"closed"} = 1;
5259 @TRACE@("found end of '$tag'");
5265 } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
5267 push @md_blocks, $md_block;
5269 my $indentation = $1;
5270 $lines =~ s/^[ ]{0,4}//;
5271 $md_block = { type => "li",
5273 indentation => $indentation,
5277 lines => [ $lines ] };
5279 } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
5280 push @md_blocks, $md_block;
5281 $md_block = { type => "quote",
5287 if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5288 push @md_blocks, $md_block;
5290 my $indentation = $1;
5291 $lines =~ s/^[ ]{0,4}//;
5293 $md_block = { type => "li",
5295 indentation => $indentation,
5296 marker => "\\d+[.]",
5299 lines => [ $lines ] };
5305 if ($md_block->{"type"} eq "paragraph") {
5306 if ($md_block->{"interrupted"}) {
5307 push @md_blocks, $md_block;
5308 $md_block = { type => "paragraph",
5311 @TRACE@("new paragraph due to interrupted");
5313 $md_block->{"text"} .= "\n" . $line;
5314 @TRACE@("add to paragraph");
5317 push @md_blocks, $md_block;
5318 $md_block = { type => "paragraph",
5320 @TRACE@("new paragraph due to different block type");
5324 push @md_blocks, $md_block;
5331 sub MarkDownParseSpanElementsInner {
5332 my ($text, $markersref) = @_;
5334 my %markers = map { $_ => 1 } @$markersref;
5336 while ($text ne "") {
5337 my $closest_marker = "";
5338 my $closest_marker_index = 0;
5339 my $closest_marker_position = -1;
5340 my $text_marker = "";
5347 while ( ($marker, $use) = each %markers ) {
5348 my $marker_position;
5354 $marker_position = index ($text, $marker);
5356 if ($marker_position < 0) {
5357 $markers{$marker} = 0;
5361 if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5362 $closest_marker = $marker;
5363 $closest_marker_index = $i;
5364 $closest_marker_position = $marker_position;
5368 if ($closest_marker_position >= 0) {
5369 $text_marker = substr ($text, $closest_marker_position);
5372 if ($text_marker eq "") {
5378 $markup .= substr ($text, 0, $closest_marker_position);
5379 $text = substr ($text, $closest_marker_position);
5380 @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5382 if ($closest_marker eq "![" || $closest_marker eq "[") {
5385 if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5388 %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5391 $offset = length ($&);
5392 if ($element{"!"}) {
5396 $remaining_text = substr ($text, $offset);
5397 if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5402 $offset += length ($&);
5403 } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5404 $element{"ref"} = $1;
5405 $offset += length ($&);
5412 if ($element{"»"}) {
5413 $element{"»"} =~ s/&/&/g;
5414 $element{"»"} =~ s/</</g;
5416 if ($element{"!"}) {
5417 $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5419 if (defined ($element{"a"})) {
5420 $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5423 $markup .= "</inlinemediaobject>";
5424 } elsif ($element{"ref"}) {
5425 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5426 $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5428 if (defined ($element{"#"})) {
5429 # title attribute not supported
5432 $markup .= ">" . $element{"a"} . "</link>";
5434 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5435 $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5437 if (defined ($element{"#"})) {
5438 # title attribute not supported
5441 $markup .= ">" . $element{"a"} . "</ulink>";
5444 $markup .= $closest_marker;
5445 if ($closest_marker eq "![") {
5451 } elsif ($closest_marker eq "<") {
5452 if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5453 my $element_url = $1;
5454 $element_url =~ s/&/&/g;
5455 $element_url =~ s/</</g;
5457 $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5458 $offset = length ($&);
5459 } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5460 $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5461 $offset = length ($&);
5462 } elsif ($text =~ /^<[^>]+?>/) {
5464 $offset = length ($&);
5469 } elsif ($closest_marker eq "\\") {
5470 my $special_char = substr ($text, 1, 1);
5471 if ($MD_ESCAPABLE_CHARS{$special_char} ||
5472 $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5473 $markup .= $special_char;
5479 } elsif ($closest_marker eq "`") {
5480 if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5481 my $element_text = $2;
5482 $markup .= "<literal>" . $element_text . "</literal>";
5483 $offset = length ($&);
5488 } elsif ($closest_marker eq "@") {
5489 # Convert '@param()'
5490 # FIXME: we could make those also links ($symbol.$2), but that would be less
5491 # useful as the link target is a few lines up or down
5492 if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5493 $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5494 $offset = length ($&);
5495 } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5496 # Convert '@param', but not '\@param'.
5497 $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5498 $offset = length ($&);
5499 } elsif ($text =~ /^\\\@/) {
5501 $offset = length ($&);
5506 } elsif ($closest_marker eq "#") {
5507 if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5508 # handle #Object.func()
5509 $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5510 $offset = length ($&);
5511 } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5512 # Convert '#symbol', but not '\#symbol'.
5513 $markup .= $1 . &MakeHashXRef ($2, "type");
5514 $offset = length ($&);
5515 } elsif ($text =~ /^\\#/) {
5517 $offset = length ($&);
5522 } elsif ($closest_marker eq "%") {
5523 if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5524 # Convert '%constant', but not '\%constant'.
5525 # Also allow negative numbers, e.g. %-1.
5526 $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5527 $offset = length ($&);
5528 } elsif ($text =~ /^\\%/) {
5530 $offset = length ($&);
5538 $text = substr ($text, $offset);
5545 sub MarkDownParseSpanElements {
5547 my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5549 $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5551 # Convert 'function()' or 'macro()'.
5552 # if there is abc_*_def() we don't want to make a link to _def()
5553 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5554 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5559 sub ReplaceEntities {
5560 my ($text, $symbol) = @_;
5562 my @entities = ( [ "<", "<" ],
5571 [ "&", "&" ] ); # Do this last, or the others get messed up.
5574 # Expand entities in <programlisting> even inside CDATA since
5575 # we changed the definition of |[ to add CDATA
5576 for ($i = 0; $i <= $#entities; $i++) {
5577 $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5583 sub MarkDownOutputDocBook {
5584 my ($blocksref, $symbol, $context) = @_;
5587 my @blocks = @$blocksref;
5589 foreach $block (@blocks) {
5593 #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
5595 if ($block->{"type"} eq "paragraph") {
5596 $text = &MarkDownParseSpanElements ($block->{"text"});
5597 if ($context eq "li" && $output eq "") {
5598 if ($block->{"interrupted"}) {
5599 $output .= "\n<para>$text</para>\n";
5601 $output .= "<para>$text</para>";
5607 $output .= "<para>$text</para>\n";
5610 } elsif ($block->{"type"} eq "heading") {
5613 $title = &MarkDownParseSpanElements ($block->{"text"});
5615 if ($block->{"level"} == 1) {
5621 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5622 if (defined ($block->{"id"})) {
5623 $output .= "<$tag id=\"" . $block->{"id"} . "\">";
5625 $output .= "<$tag>";
5628 $output .= "<title>$title</title>$text</$tag>\n";
5629 } elsif ($block->{"type"} eq "li") {
5630 my $tag = "itemizedlist";
5632 if ($block->{"first"}) {
5633 if ($block->{"ordered"}) {
5634 $tag = "orderedlist";
5636 $output .= "<$tag>\n";
5639 if ($block->{"interrupted"}) {
5640 push @{$block->{"lines"}}, "";
5643 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5644 $output .= "<listitem>".$text."</listitem>\n";
5645 if ($block->{"last"}) {
5646 if ($block->{"ordered"}) {
5647 $tag = "orderedlist";
5649 $output .= "</$tag>\n";
5651 } elsif ($block->{"type"} eq "quote") {
5652 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5653 $output .= "<blockquote>\n$text</blockquote>\n";
5654 } elsif ($block->{"type"} eq "code") {
5655 my $tag = "programlisting";
5657 if ($block->{"language"}) {
5658 if ($block->{"language"} eq "plain") {
5659 $output .= "<informalexample><screen><![CDATA[\n";
5662 $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5665 $output .= "<informalexample><programlisting><![CDATA[\n";
5667 foreach (@{$block->{"lines"}}) {
5668 $output .= &ReplaceEntities ($_, $symbol) . "\n";
5670 $output .= "]]></$tag></informalexample>\n";
5671 } elsif ($block->{"type"} eq "markup") {
5672 $text = &ExpandAbbreviations($symbol, $block->{"text"});
5673 $output .= $text."\n";
5675 $output .= $block->{"text"}."\n";
5677 #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
5683 sub MarkDownParseLines {
5684 my ($linesref, $symbol, $context) = @_;
5686 my @lines = @$linesref;
5689 @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5690 $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5696 my ($text, $symbol) = @_;
5699 # take out some variability in line endings
5700 $text =~ s%\r\n%\n%g;
5704 @lines = split("\n", $text);
5705 $text = MarkDownParseLines(\@lines, $symbol, "");
5710 #############################################################################
5711 # LIBRARY FUNCTIONS - These functions are used in both gtkdoc-mkdb and
5712 # gtkdoc-mktmpl and should eventually be moved to a
5714 #############################################################################
5716 #############################################################################
5717 # Function : ReadDeclarationsFile
5718 # Description : This reads in a file containing the function/macro/enum etc.
5721 # Note that in some cases there are several declarations with
5722 # the same name, e.g. for conditional macros. In this case we
5723 # set a flag in the %DeclarationConditional hash so the
5724 # declaration is not shown in the docs.
5726 # If a macro and a function have the same name, e.g. for
5727 # gtk_object_ref, the function declaration takes precedence.
5729 # Some opaque structs are just declared with 'typedef struct
5730 # _name name;' in which case the declaration may be empty.
5731 # The structure may have been found later in the header, so
5732 # that overrides the empty declaration.
5734 # Arguments : $file - the declarations file to read
5735 # $override - if declarations in this file should override
5736 # any current declaration.
5737 #############################################################################
5739 sub ReadDeclarationsFile {
5740 my ($file, $override) = @_;
5742 if ($override == 0) {
5744 %DeclarationTypes = ();
5745 %DeclarationConditional = ();
5746 %DeclarationOutput = ();
5750 || die "Can't open $file: $!";
5751 my $declaration_type = "";
5752 my $declaration_name;
5754 my $is_deprecated = 0;
5756 if (!$declaration_type) {
5757 if (m/^<([^>]+)>/) {
5758 $declaration_type = $1;
5759 $declaration_name = "";
5760 @TRACE@("Found declaration: $declaration_type\n");
5764 if (m%^<NAME>(.*)</NAME>%) {
5765 $declaration_name = $1;
5766 } elsif (m%^<DEPRECATED/>%) {
5768 } elsif (m%^</$declaration_type>%) {
5769 @TRACE@("Found end of declaration: $declaration_name\n");
5770 # Check that the declaration has a name
5771 if ($declaration_name eq "") {
5772 &LogWarning ($file, $., "$declaration_type has no name.\n");
5775 # If the declaration is an empty typedef struct _XXX XXX
5776 # set the flag to indicate the struct has a typedef.
5777 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5778 && $declaration =~ m/^\s*$/) {
5779 @TRACE@("Struct has typedef: $declaration_name\n");
5780 $StructHasTypedef{$declaration_name} = 1;
5783 # Check if the symbol is already defined.
5784 if (defined ($Declarations{$declaration_name})
5785 && $override == 0) {
5786 # Function declarations take precedence.
5787 if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5789 } elsif ($declaration_type eq 'FUNCTION') {
5790 if ($is_deprecated) {
5791 $Deprecated{$declaration_name} = "";
5793 $Declarations{$declaration_name} = $declaration;
5794 $DeclarationTypes{$declaration_name} = $declaration_type;
5795 } elsif ($DeclarationTypes{$declaration_name}
5796 eq $declaration_type) {
5797 # If the existing declaration is empty, or is just a
5798 # forward declaration of a struct, override it.
5799 if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5800 if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5801 if ($is_deprecated) {
5802 $Deprecated{$declaration_name} = "";
5804 $Declarations{$declaration_name} = $declaration;
5805 } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5806 # Ignore an empty or forward declaration.
5808 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5811 # set flag in %DeclarationConditional hash for
5812 # multiply defined macros/typedefs.
5813 $DeclarationConditional{$declaration_name} = 1;
5816 &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5819 if ($is_deprecated) {
5820 $Deprecated{$declaration_name} = "";
5822 $Declarations{$declaration_name} = $declaration;
5823 $DeclarationTypes{$declaration_name} = $declaration_type;
5826 $declaration_type = "";
5837 #############################################################################
5838 # Function : ReadSignalsFile
5839 # Description : This reads in an existing file which contains information on
5840 # all GTK signals. It creates the arrays @SignalNames and
5841 # @SignalPrototypes containing info on the signals. The first
5842 # line of the SignalPrototype is the return type of the signal
5843 # handler. The remaining lines are the parameters passed to it.
5844 # The last parameter, "gpointer user_data" is always the same
5845 # so is not included.
5846 # Arguments : $file - the file containing the signal handler prototype
5848 #############################################################################
5850 sub ReadSignalsFile {
5858 my $signal_prototype;
5860 # Reset the signal info.
5861 @SignalObjects = ();
5863 @SignalReturns = ();
5865 @SignalPrototypes = ();
5870 if (!open (INPUT, $file)) {
5871 warn "Can't open $file - skipping signals\n";
5878 $signal_object = "";
5880 $signal_returns = "";
5881 $signal_prototype = "";
5884 if (m/^<NAME>(.*)<\/NAME>/) {
5886 if ($signal_name =~ m/^(.*)::(.*)$/) {
5887 $signal_object = $1;
5888 ($signal_name = $2) =~ s/_/-/g;
5889 @TRACE@("Found signal: $signal_name\n");
5891 &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5893 } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5894 $signal_returns = $1;
5895 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5897 } elsif (m%^</SIGNAL>%) {
5898 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5899 push (@SignalObjects, $signal_object);
5900 push (@SignalNames, $signal_name);
5901 push (@SignalReturns, $signal_returns);
5902 push (@SignalFlags, $signal_flags);
5903 push (@SignalPrototypes, $signal_prototype);
5906 $signal_prototype .= $_;
5914 #############################################################################
5915 # Function : ReadTemplateFile
5916 # Description : This reads in the manually-edited documentation file
5917 # corresponding to the file currently being created, so we can
5918 # insert the documentation at the appropriate places.
5919 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5920 # is a hash of arrays.
5921 # NOTE: This function is duplicated in gtkdoc-mktmpl (but
5922 # slightly different).
5923 # Arguments : $docsfile - the template file to read in.
5924 # $skip_unused_params - 1 if the unused parameters should be
5926 #############################################################################
5928 sub ReadTemplateFile {
5929 my ($docsfile, $skip_unused_params) = @_;
5931 my $template = "$docsfile.sgml";
5932 if (! -f $template) {
5933 @TRACE@("File doesn't exist: $template\n");
5937 # start with empty hashes, we merge the source comment for each file
5943 my $current_type = ""; # Type of symbol being read.
5944 my $current_symbol = ""; # Name of symbol being read.
5945 my $symbol_doc = ""; # Description of symbol being read.
5946 my @params; # Parameter names and descriptions of current
5947 # function/macro/function typedef.
5948 my $current_param = -1; # Index of parameter currently being read.
5949 # Note that the param array contains pairs
5950 # of param name & description.
5951 my $in_unused_params = 0; # True if we are reading in the unused params.
5952 my $in_deprecated = 0;
5954 my $in_stability = 0;
5956 open (DOCS, "$template")
5957 || die "Can't open $template: $!";
5959 @TRACE@("reading template $template");
5962 if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5965 if ($symbol eq "Title"
5966 || $symbol eq "Short_Description"
5967 || $symbol eq "Long_Description"
5968 || $symbol eq "See_Also"
5969 || $symbol eq "Stability_Level"
5970 || $symbol eq "Include"
5971 || $symbol eq "Image") {
5973 $symbol = $docsfile . ":" . $symbol;
5976 @TRACE@("Found symbol: $symbol\n");
5977 # Remember file and line for the symbol
5978 $SymbolSourceFile{$symbol} = $template;
5979 $SymbolSourceLine{$symbol} = $.;
5981 # Store previous symbol, but remove any trailing blank lines.
5982 if ($current_symbol ne "") {
5983 $symbol_doc =~ s/\s+$//;
5984 $SymbolTypes{$current_symbol} = $current_type;
5985 $SymbolDocs{$current_symbol} = $symbol_doc;
5987 # Check that the stability level is valid.
5988 if ($StabilityLevel{$current_symbol}) {
5989 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5992 if ($current_param >= 0) {
5993 $SymbolParams{$current_symbol} = [ @params ];
5995 # Delete any existing params in case we are overriding a
5996 # previously read template.
5997 delete $SymbolParams{$current_symbol};
6000 $current_type = $type;
6001 $current_symbol = $symbol;
6002 $current_param = -1;
6003 $in_unused_params = 0;
6010 } elsif (m/^<!-- # Unused Parameters # -->/) {
6011 @TRACE@("Found unused parameters\n");
6012 $in_unused_params = 1;
6015 } elsif ($in_unused_params && $skip_unused_params) {
6016 # When outputting the DocBook we skip unused parameters.
6017 @TRACE@("Skipping unused param: $_");
6021 # Check if param found. Need to handle "..." and "format...".
6022 if (s/^\@([\w\.]+):\040?//) {
6023 my $param_name = $1;
6024 my $param_desc = $_;
6025 # Allow variations of 'Returns'
6026 if ($param_name =~ m/^[Rr]eturns?$/) {
6027 $param_name = "Returns";
6029 # Allow varargs variations
6030 if ($param_name =~ m/^.*\.\.\.$/) {
6031 $param_name = "...";
6034 # strip trailing whitespaces and blank lines
6037 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
6039 if ($param_name eq "Deprecated") {
6041 $Deprecated{$current_symbol} = $_;
6042 } elsif ($param_name eq "Since") {
6045 $Since{$current_symbol} = $_;
6046 } elsif ($param_name eq "Stability") {
6048 $StabilityLevel{$current_symbol} = $_;
6050 push (@params, $param_name);
6051 push (@params, $param_desc);
6052 $current_param += $PARAM_FIELD_COUNT;
6055 # strip trailing whitespaces and blank lines
6060 if ($in_deprecated) {
6061 $Deprecated{$current_symbol} .= $_;
6062 } elsif ($in_since) {
6063 &LogWarning ($template, $., "multi-line since docs found");
6064 #$Since{$current_symbol} .= $_;
6065 } elsif ($in_stability) {
6066 $StabilityLevel{$current_symbol} .= $_;
6067 } elsif ($current_param >= 0) {
6068 $params[$current_param] .= $_;
6077 # Remember to finish the current symbol doccs.
6078 if ($current_symbol ne "") {
6080 $symbol_doc =~ s/\s+$//;
6081 $SymbolTypes{$current_symbol} = $current_type;
6082 $SymbolDocs{$current_symbol} = $symbol_doc;
6084 # Check that the stability level is valid.
6085 if ($StabilityLevel{$current_symbol}) {
6086 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
6089 if ($current_param >= 0) {
6090 $SymbolParams{$current_symbol} = [ @params ];
6092 # Delete any existing params in case we are overriding a
6093 # previously read template.
6094 delete $SymbolParams{$current_symbol};
6103 #############################################################################
6104 # Function : ReadObjectHierarchy
6105 # Description : This reads in the $MODULE-hierarchy.txt file containing all
6106 # the GtkObject subclasses described in this module (and their
6108 # It places them in the @Objects array, and places their level
6109 # in the object hierarchy in the @ObjectLevels array, at the
6110 # same index. GtkObject, the root object, has a level of 1.
6112 # This also generates tree_index.sgml as it goes along.
6115 #############################################################################
6117 sub ReadObjectHierarchy {
6121 if (! -f $OBJECT_TREE_FILE) {
6124 if (!open (INPUT, $OBJECT_TREE_FILE)) {
6125 warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
6129 # Only emit objects if they are supposed to be documented, or if
6130 # they have documented children. To implement this, we maintain a
6131 # stack of pending objects which will be emitted if a documented
6133 my @pending_objects = ();
6134 my @pending_levels = ();
6140 my $level = (length($`)) / 2 + 1;
6147 while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
6148 my $pobject = pop(@pending_objects);
6149 my $plevel = pop(@pending_levels);
6152 push (@pending_objects, $object);
6153 push (@pending_levels, $level);
6155 if (exists($KnownSymbols{$object})) {
6156 while ($#pending_levels >= 0) {
6157 $object = shift @pending_objects;
6158 $level = shift @pending_levels;
6159 $xref = &MakeXRef ($object);
6161 push (@tree, ' ' x ($level * 4) . "$xref");
6162 push (@Objects, $object);
6163 push (@ObjectLevels, $level);
6164 $ObjectRoots{$object} = $root;
6168 # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
6175 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
6176 my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
6177 my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
6179 open (OUTPUT, ">$new_tree_index")
6180 || die "Can't create $new_tree_index: $!";
6182 print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
6185 &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
6190 #############################################################################
6191 # Function : ReadInterfaces
6192 # Description : This reads in the $MODULE.interfaces file.
6195 #############################################################################
6197 sub ReadInterfaces {
6200 if (! -f $INTERFACES_FILE) {
6203 if (!open (INPUT, $INTERFACES_FILE)) {
6204 warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
6210 my ($object, @ifaces) = split;
6211 if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
6212 my @knownIfaces = ();
6214 # filter out private interfaces, but leave foreign interfaces
6215 foreach my $iface (@ifaces) {
6216 if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
6217 push (@knownIfaces, $iface);
6221 $Interfaces{$object} = join(' ', @knownIfaces);
6222 @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
6224 @TRACE@("skipping interfaces for unknown symbol: $object\n");
6230 #############################################################################
6231 # Function : ReadPrerequisites
6232 # Description : This reads in the $MODULE.prerequisites file.
6235 #############################################################################
6237 sub ReadPrerequisites {
6238 %Prerequisites = ();
6240 if (! -f $PREREQUISITES_FILE) {
6243 if (!open (INPUT, $PREREQUISITES_FILE)) {
6244 warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
6250 my ($iface, @prereqs) = split;
6251 if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
6252 my @knownPrereqs = ();
6254 # filter out private prerequisites, but leave foreign prerequisites
6255 foreach my $prereq (@prereqs) {
6256 if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
6257 push (@knownPrereqs, $prereq);
6261 $Prerequisites{$iface} = join(' ', @knownPrereqs);
6267 #############################################################################
6268 # Function : ReadArgsFile
6269 # Description : This reads in an existing file which contains information on
6270 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
6271 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
6273 # Arguments : $file - the file containing the arg information.
6274 #############################################################################
6289 # Reset the args info.
6302 if (!open (INPUT, $file)) {
6303 warn "Can't open $file - skipping args\n";
6320 if (m/^<NAME>(.*)<\/NAME>/) {
6322 if ($arg_name =~ m/^(.*)::(.*)$/) {
6324 ($arg_name = $2) =~ s/_/-/g;
6325 @TRACE@("Found arg: $arg_name\n");
6327 &LogWarning ($file, $., "Invalid argument name: $arg_name");
6329 } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6331 } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6333 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6335 } elsif (m/^<NICK>(.*)<\/NICK>/) {
6337 } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6339 if ($arg_blurb eq "(null)") {
6341 &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6343 } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6345 } elsif (m%^</ARG>%) {
6346 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6347 push (@ArgObjects, $arg_object);
6348 push (@ArgNames, $arg_name);
6349 push (@ArgTypes, $arg_type);
6350 push (@ArgRanges, $arg_range);
6351 push (@ArgFlags, $arg_flags);
6352 push (@ArgNicks, $arg_nick);
6353 push (@ArgBlurbs, $arg_blurb);
6354 push (@ArgDefaults, $arg_default);
6362 #############################################################################
6363 # Function : AddTreeLineArt
6364 # Description : Add unicode lineart to a pre-indented string array and returns
6365 # it as as multiline string.
6366 # Arguments : @tree - array of indented strings.
6367 #############################################################################
6369 sub AddTreeLineArt {
6370 my @tree = @{$_[0]};
6375 # iterate bottom up over the tree
6376 for ($i = $#tree; $i >= 0; $i--) {
6377 # count leading spaces
6378 $tree[$i] =~ /^([^<A-Za-z]*)/;
6379 $indent = length( $1 );
6380 # replace with ╰───, if place of ╰ is not space insert ├
6382 if (substr($tree[$i],$indent-4,1) eq " ") {
6383 substr($tree[$i],$indent-4,4) = "--- ";
6385 substr($tree[$i],$indent-4,4) = "+-- ";
6387 # go lines up while space and insert |
6388 for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6389 substr($tree[$j],$indent-4,1) = '|';
6394 my $res = join("\n", @tree);
6395 # unicode chars for: ╰──
6396 $res =~ s%---%<phrase role=\"lineart\">╰──</phrase>%g;
6397 # unicde chars for: ├──
6398 $res =~ s%\+--%<phrase role=\"lineart\">├──</phrase>%g;
6399 # unicode char for: │
6400 $res =~ s%\|%<phrase role=\"lineart\">│</phrase>%g;
6406 #############################################################################
6407 # Function : CheckIsObject
6408 # Description : Returns 1 if the given name is a GObject or a subclass.
6409 # It uses the global @Objects array.
6410 # Note that the @Objects array only contains classes in the
6411 # current module and their ancestors - not all GObject classes.
6412 # Arguments : $name - the name to check.
6413 #############################################################################
6417 my $root = $ObjectRoots{$name};
6418 # Let GBoxed pass as an object here to get -struct appended to the id
6419 # and prevent conflicts with sections.
6420 return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6424 #############################################################################
6425 # Function : MakeReturnField
6426 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6427 # Arguments : $str - the string to pad.
6428 #############################################################################
6430 sub MakeReturnField {
6433 return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6436 #############################################################################
6437 # Function : GetSymbolSourceFile
6438 # Description : Get the filename where the symbol docs where taken from.
6439 # Arguments : $symbol - the symbol name
6440 #############################################################################
6442 sub GetSymbolSourceFile {
6445 if (defined($SourceSymbolSourceFile{$symbol})) {
6446 return $SourceSymbolSourceFile{$symbol};
6447 } elsif (defined($SymbolSourceFile{$symbol})) {
6448 return $SymbolSourceFile{$symbol};
6454 #############################################################################
6455 # Function : GetSymbolSourceLine
6456 # Description : Get the file line where the symbol docs where taken from.
6457 # Arguments : $symbol - the symbol name
6458 #############################################################################
6460 sub GetSymbolSourceLine {
6463 if (defined($SourceSymbolSourceLine{$symbol})) {
6464 return $SourceSymbolSourceLine{$symbol};
6465 } elsif (defined($SymbolSourceLine{$symbol})) {
6466 return $SymbolSourceLine{$symbol};