4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998 Damon Chaplin
6 # 2007-2016 Stefan Sauer
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 # the regexp that parses the annotation is in ScanSourceFile()
156 my %AnnotationDefinition = (
157 # the GObjectIntrospection annotations are defined at:
158 # https://live.gnome.org/GObjectIntrospection/Annotations
159 'allow-none' => "NULL is OK, both for passing and for returning.",
160 'nullable' => "NULL may be passed as the value in, out, in-out; or as a return value.",
161 'not nullable' => "NULL must not be passed as the value in, out, in-out; or as a return value.",
162 'optional' => "NULL may be passed instead of a pointer to a location.",
163 'array' => "Parameter points to an array of items.",
164 'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
165 'attributes' => "Free-form key-value pairs.",
166 'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
167 'constructor' => "This symbol is a constructor, not a static method.",
168 'destroy' => "This parameter is a 'destroy_data', for callbacks.",
169 'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
170 'element-type' => "Generics and defining elements of containers and arrays.",
171 'error-domains' => "Typed errors. Similar to throws in Java.",
172 'foreign' => "This is a foreign struct.",
173 'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
174 'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
175 'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
176 'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
177 'method' => "This is a method",
178 'not-error' => "A GError parameter is not to be handled like a normal GError.",
179 'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
180 'out caller-allocates' => "Out parameter, where caller must allocate storage.",
181 'out callee-allocates' => "Out parameter, where caller must allocate storage.",
182 'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
183 'rename-to' => "Rename the original symbol's name to SYMBOL.",
184 'scope call' => "The callback is valid only during the call to the method.",
185 'scope async' => "The callback is valid until first called.",
186 'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
187 'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
188 'skip' => "Exposed in C code, not necessarily available in other languages.",
189 'transfer container' => "Free data container after the code is done.",
190 'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
191 'transfer full' => "Free data after the code is done.",
192 'transfer none' => "Don't free data after the code is done.",
193 'type' => "Override the parsed C type with given type.",
194 'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
195 'virtual' => "This is the invoker for a virtual method.",
196 'value' => "The specified value overrides the evaluated value of the constant.",
197 # Stability Level definition
198 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
200 The intention of a Stable interface is to enable arbitrary third parties to
201 develop applications to these interfaces, release them, and have confidence that
202 they will run on all minor releases of the product (after the one in which the
203 interface was introduced, and within the same major release). Even at a major
204 release, incompatible changes are expected to be rare, and to have strong
208 Unstable interfaces are experimental or transitional. They are typically used to
209 give outside developers early access to new or rapidly changing technology, or
210 to provide an interim solution to a problem where a more general solution is
211 anticipated. No claims are made about either source or binary compatibility from
212 one minor release to the next.
214 The Unstable interface level is a warning that these interfaces are subject to
215 change without warning and should not be used in unbundled products.
217 Given such caveats, customer impact need not be a factor when considering
218 incompatible changes to an Unstable interface in a major or minor release.
219 Nonetheless, when such changes are introduced, the changes should still be
220 mentioned in the release notes for the affected release.
223 An interface that can be used within the GNOME stack itself, but that is not
224 documented for end-users. Such functions should only be used in specified and
229 # Elements to consider non-block items in MarkDown parsing
230 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
244 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
260 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
263 # Function and other declaration output settings.
264 my $RETURN_TYPE_FIELD_WIDTH = 20;
265 my $SYMBOL_FIELD_WIDTH = 36;
266 my $MAX_SYMBOL_FIELD_WIDTH = 40;
267 my $SIGNAL_FIELD_WIDTH = 16;
268 my $PARAM_FIELD_COUNT = 2;
270 # XML, SGML formatting helper
274 Run() unless caller; # Run program unless loaded as a module
278 my %optctl = ('module' => \$MODULE,
279 'source-dir' => \@SOURCE_DIRS,
280 'source-suffixes' => \$SOURCE_SUFFIXES,
281 'ignore-files' => \$IGNORE_FILES,
282 'output-dir' => \$DB_OUTPUT_DIR,
283 'tmpl-dir' => \$TMPL_DIR,
284 'version' => \$PRINT_VERSION,
285 'help' => \$PRINT_HELP,
286 'main-sgml-file' => \$MAIN_SGML_FILE,
287 'expand-content-files' => \$EXPAND_CONTENT_FILES,
288 'sgml-mode' => \$INLINE_MARKUP_MODE,
289 'xml-mode' => \$INLINE_MARKUP_MODE,
290 'default-stability' => \$DEFAULT_STABILITY,
291 'default-includes' => \$DEFAULT_INCLUDES,
292 'output-format' => \$OUTPUT_FORMAT,
293 'name-space' => \$NAME_SPACE,
294 'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
295 'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
297 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
298 "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version",
299 "outputallsymbols", "outputsymbolswithoutsince",
300 "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
301 "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
302 "output-format:s", "name-space:s");
304 if ($PRINT_VERSION) {
313 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
314 && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
320 gtkdoc-mkdb version @VERSION@ - generate docbook files
322 --module=MODULE_NAME Name of the doc module being parsed
323 --source-dir=DIRNAME Directories which contain inline reference material
324 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
325 --ignore-files=FILES A space-separated list of header files/dirs not to
327 --output-dir=DIRNAME Directory to put the generated DocBook files in
328 --tmpl-dir=DIRNAME Directory in which template files may be found
329 --main-sgml-file=FILE File containing the toplevel DocBook file.
330 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
331 --output-format=FORMAT Format to use for the generated docbook, XML or SGML.
332 --{xml,sgml}-mode Allow DocBook markup in inline documentation.
333 --default-stability=LEVEL Specify default stability Level. Valid values are
334 Stable, Unstable, or Private.
335 --default-includes=FILENAMES Specify default includes for section Synopsis
336 --name-space=NS Omit namespace in index.
337 --version Print the version of this program
338 --help Print this help
343 @TRACE@(" ignore files: [$IGNORE_FILES]\n");
345 # check output format
346 if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
347 $OUTPUT_FORMAT = "xml";
349 $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
351 if ($OUTPUT_FORMAT ne "xml") {
352 die "Invalid format '$OUTPUT_FORMAT' passed to --output.format"
355 if (!$MAIN_SGML_FILE) {
356 # backwards compatibility
357 if (-e "${MODULE}-docs.sgml") {
358 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
360 $MAIN_SGML_FILE = "${MODULE}-docs.xml";
364 # extract docbook header or define default
365 if (-e $MAIN_SGML_FILE) {
366 open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
367 $doctype_header = "";
369 if (/^\s*<(book|chapter|article)/) {
370 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
371 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
372 $doctype_header = "";
376 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
377 # FIXME: not sure if we can do this now, as people already work-around the problem
378 # s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#;
379 s#<!ENTITY % gtkdocentities SYSTEM \"([^"]*)\">#<!ENTITY % gtkdocentities SYSTEM \"../$1\">#;
380 $doctype_header .= $_;
384 $doctype_header = <<EOF;
385 <?xml version="1.0"?>
386 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
387 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
389 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
390 <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
395 chomp($doctype_header);
397 # All the files are written in subdirectories beneath here.
398 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
400 # This is where we put all the DocBook output.
401 $DB_OUTPUT_DIR = $DB_OUTPUT_DIR ? $DB_OUTPUT_DIR : "$ROOT_DIR/xml";
403 # This file contains the object hierarchy.
404 $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
406 # This file contains the interfaces.
407 $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
409 # This file contains the prerequisites.
410 $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
412 # This file contains signal arguments and names.
413 $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
415 # The file containing Arg information.
416 $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
418 # Create the root DocBook output directory if it doens't exist.
419 if (! -e $DB_OUTPUT_DIR) {
420 mkdir ("$DB_OUTPUT_DIR", 0777)
421 || die "Can't create directory: $DB_OUTPUT_DIR";
424 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
425 &ReadSignalsFile ($SIGNALS_FILE);
426 &ReadArgsFile ($ARGS_FILE);
427 &ReadObjectHierarchy;
431 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
432 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
433 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
436 for my $dir (@SOURCE_DIRS) {
437 &ReadSourceDocumentation ($dir);
440 my $changed = &OutputDB ("$ROOT_DIR/$MODULE-sections.txt");
442 # If any of the DocBook files have changed, update the timestamp file (so
443 # it can be used for Makefile dependencies).
444 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
446 # try to detect the common prefix
447 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
448 if ($NAME_SPACE eq "") {
455 foreach my $symbol (keys(%IndexEntriesFull)) {
456 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
457 if (length($symbol)>$pos) {
458 $letter=substr($symbol,$pos,1);
459 # stop prefix scanning
460 if ($letter eq "_") {
464 # Should we also stop on a uppercase char, if last was lowercase
465 # GtkWidget, if we have the 'W' and had the 't' before
466 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
467 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
468 # need to recound each time as this is per symbol
469 $prefix{uc($letter)}++;
473 if ($letter ne "" && $letter ne "_") {
476 foreach $letter (keys(%prefix)) {
477 #print "$letter: $prefix{$letter}.\n";
478 if ($prefix{$letter}>$maxsymbols) {
480 $maxsymbols=$prefix{$letter};
483 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
484 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
487 $NAME_SPACE .= $maxletter;
494 } while ($ratio > 0.9);
495 #print "most symbols start with $NAME_SPACE\n";
499 &OutputDeprecatedIndex;
501 &OutputAnnotationGlossary;
503 open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
504 || die "Can't create $ROOT_DIR/sgml.stamp: $!";
505 print (TIMESTAMP "timestamp");
510 #############################################################################
511 # Function : OutputObjectList
512 # Description : This outputs the alphabetical list of objects, in a columned
514 # FIXME: Currently this also outputs ancestor objects
515 # which may not actually be in this module.
517 #############################################################################
519 sub OutputObjectList {
523 # my $old_object_index = "$DB_OUTPUT_DIR/object_index.xml";
524 my $old_object_index = "$DB_OUTPUT_DIR/object_index.sgml";
525 my $new_object_index = "$DB_OUTPUT_DIR/object_index.new";
527 open (OUTPUT, ">$new_object_index")
528 || die "Can't create $new_object_index: $!";
530 print (OUTPUT <<EOF);
531 ${\( MakeDocHeader ("informaltable") )}
532 <informaltable pgwide="1" frame="none">
533 <tgroup cols="$cols">
534 <colspec colwidth="1*"/>
535 <colspec colwidth="1*"/>
536 <colspec colwidth="1*"/>
542 foreach $object (sort (@Objects)) {
543 my $xref = &MakeXRef ($object);
544 if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
545 print (OUTPUT "<entry>$xref</entry>\n");
546 if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
550 # emit an empty row, since empty tables are invalid
551 print (OUTPUT "<row><entry> </entry></row>\n");
554 if ($count % $cols > 0) {
555 print (OUTPUT "</row>\n");
559 print (OUTPUT <<EOF);
560 </tbody></tgroup></informaltable>
564 &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
567 #############################################################################
568 # Function : TrimTextBlock
569 # Description : Trims extra whitespace. Empty lines inside a block are
571 # Arguments : $desc - the text block to trim. May contain newlines.
572 #############################################################################
577 # strip leading spaces on the block
579 # strip trailing spaces on every line
580 $desc =~ s/\s+$/\n/mg;
586 #############################################################################
587 # Function : OutputDB
588 # Description : This collects the output for each section of the docs, and
589 # outputs each file when the end of the section is found.
590 # Arguments : $file - the $MODULE-sections.txt file which contains all of
591 # the functions/macros/structs etc. being documented, organised
592 # into sections and subsections.
593 #############################################################################
598 @TRACE@("Reading: $file\n");
600 || die "Can't open $file: $!";
603 my $book_bottom = "";
604 my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
605 my $section_includes = "";
612 my $functions_synop = "";
613 my $other_synop = "";
614 my $functions_details = "";
615 my $other_details = "";
616 my $signals_synop = "";
617 my $signals_desc = "";
619 my $child_args_synop = "";
620 my $style_args_synop = "";
622 my $child_args_desc = "";
623 my $style_args_desc = "";
624 my $hierarchy_str = "";
627 my $implementations = "";
628 my $prerequisites = "";
630 my @file_objects = ();
632 my %symbol_def_line = ();
634 # merge the source docs, in case there are no templates
635 &MergeSourceDocumentation;
641 } elsif (m/^<SECTION>/) {
645 %symbol_def_line = ();
647 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
648 $other_synop .= "\n";
649 $functions_synop .= "\n";
652 } elsif (m/^<SUBSECTION>/) {
654 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
656 @TRACE@("Section: $title\n");
658 # We don't want warnings if object & class structs aren't used.
659 $DeclarationOutput{$title} = 1;
660 $DeclarationOutput{"${title}Class"} = 1;
661 $DeclarationOutput{"${title}Iface"} = 1;
662 $DeclarationOutput{"${title}Interface"} = 1;
664 } elsif (m/^<FILE>(.*)<\/FILE>/) {
666 if (! defined $templates{$filename}) {
667 if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
668 &MergeSourceDocumentation;
669 $templates{$filename}=$.;
672 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
673 "Previous occurrence on line ".$templates{$filename}.".");
675 if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
676 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
677 # Remove trailing blanks
681 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
683 $section_includes = $1;
685 if (defined $DEFAULT_INCLUDES) {
686 &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
693 } elsif (m/^<\/SECTION>/) {
694 @TRACE@("End of section: $title\n");
695 if ($num_symbols > 0) {
697 $book_bottom .= " <xi:include href=\"xml/$filename.xml\"/>\n";
699 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
700 if ($section_includes) {
701 &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
703 $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
705 if ($section_includes eq "") {
706 $section_includes = $includes;
709 $signals_synop =~ s/^\n*//g;
710 $signals_synop =~ s/\n+$/\n/g;
711 if ($signals_synop ne '') {
712 $signals_synop = <<EOF;
713 <refsect1 id="$section_id.signals" role="signal_proto">
714 <title role="signal_proto.title">Signals</title>
715 <informaltable frame="none">
717 <colspec colname="signals_return" colwidth="150px"/>
718 <colspec colname="signals_name" colwidth="300px"/>
719 <colspec colname="signals_flags" colwidth="200px"/>
727 $signals_desc = TrimTextBlock($signals_desc);
728 $signals_desc = <<EOF;
729 <refsect1 id="$section_id.signal-details" role="signals">
730 <title role="signals.title">Signal Details</title>
736 $args_synop =~ s/^\n*//g;
737 $args_synop =~ s/\n+$/\n/g;
738 if ($args_synop ne '') {
740 <refsect1 id="$section_id.properties" role="properties">
741 <title role="properties.title">Properties</title>
742 <informaltable frame="none">
744 <colspec colname="properties_type" colwidth="150px"/>
745 <colspec colname="properties_name" colwidth="300px"/>
746 <colspec colname="properties_flags" colwidth="200px"/>
754 $args_desc = TrimTextBlock($args_desc);
756 <refsect1 id="$section_id.property-details" role="property_details">
757 <title role="property_details.title">Property Details</title>
763 $child_args_synop =~ s/^\n*//g;
764 $child_args_synop =~ s/\n+$/\n/g;
765 if ($child_args_synop ne '') {
766 $args_synop .= <<EOF;
767 <refsect1 id="$section_id.child-properties" role="child_properties">
768 <title role="child_properties.title">Child Properties</title>
769 <informaltable frame="none">
771 <colspec colname="child_properties_type" colwidth="150px"/>
772 <colspec colname="child_properties_name" colwidth="300px"/>
773 <colspec colname="child_properties_flags" colwidth="200px"/>
781 $child_args_desc = TrimTextBlock($child_args_desc);
783 <refsect1 id="$section_id.child-property-details" role="child_property_details">
784 <title role="child_property_details.title">Child Property Details</title>
790 $style_args_synop =~ s/^\n*//g;
791 $style_args_synop =~ s/\n+$/\n/g;
792 if ($style_args_synop ne '') {
793 $args_synop .= <<EOF;
794 <refsect1 id="$section_id.style-properties" role="style_properties">
795 <title role="style_properties.title">Style Properties</title>
796 <informaltable frame="none">
798 <colspec colname="style_properties_type" colwidth="150px"/>
799 <colspec colname="style_properties_name" colwidth="300px"/>
800 <colspec colname="style_properties_flags" colwidth="200px"/>
808 $style_args_desc = TrimTextBlock($style_args_desc);
810 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
811 <title role="style_properties_details.title">Style Property Details</title>
817 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
818 if ($hierarchy_str ne "") {
819 $hierarchy_str = <<EOF;
820 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
821 <title role="object_hierarchy.title">Object Hierarchy</title>
822 <screen>$hierarchy_str
828 $interfaces =~ TrimTextBlock($interfaces);
829 if ($interfaces ne "") {
831 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
832 <title role="impl_interfaces.title">Implemented Interfaces</title>
838 $implementations = TrimTextBlock($implementations);
839 if ($implementations ne "") {
840 $implementations = <<EOF;
841 <refsect1 id="$section_id.implementations" role="implementations">
842 <title role="implementations.title">Known Implementations</title>
848 $prerequisites = TrimTextBlock($prerequisites);
849 if ($prerequisites ne "") {
850 $prerequisites = <<EOF;
851 <refsect1 id="$section_id.prerequisites" role="prerequisites">
852 <title role="prerequisites.title">Prerequisites</title>
858 $derived = TrimTextBlock($derived);
859 if ($derived ne "") {
861 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
862 <title role="derived_interfaces.title">Known Derived Interfaces</title>
868 $functions_synop =~ s/^\n*//g;
869 $functions_synop =~ s/\n+$/\n/g;
870 if ($functions_synop ne '') {
871 $functions_synop = <<EOF;
872 <refsect1 id="$section_id.functions" role="functions_proto">
873 <title role="functions_proto.title">Functions</title>
874 <informaltable pgwide="1" frame="none">
876 <colspec colname="functions_return" colwidth="150px"/>
877 <colspec colname="functions_name"/>
887 $other_synop =~ s/^\n*//g;
888 $other_synop =~ s/\n+$/\n/g;
889 if ($other_synop ne '') {
890 $other_synop = <<EOF;
891 <refsect1 id="$section_id.other" role="other_proto">
892 <title role="other_proto.title">Types and Values</title>
893 <informaltable role="enum_members_table" pgwide="1" frame="none">
895 <colspec colname="name" colwidth="150px"/>
896 <colspec colname="description"/>
906 my $file_changed = &OutputDBFile ($filename, $title, $section_id,
908 \$functions_synop, \$other_synop,
909 \$functions_details, \$other_details,
910 \$signals_synop, \$signals_desc,
911 \$args_synop, \$args_desc,
912 \$hierarchy_str, \$interfaces,
914 \$prerequisites, \$derived,
924 $section_includes = "";
925 $functions_synop = "";
927 $functions_details = "";
932 $child_args_synop = "";
933 $style_args_synop = "";
935 $child_args_desc = "";
936 $style_args_desc = "";
940 $implementations = "";
944 } elsif (m/^(\S+)/) {
946 @TRACE@(" Symbol: $symbol in subsection: $subsection\n");
948 # check for duplicate entries
949 if (! defined $symbol_def_line{$symbol}) {
950 my $declaration = $Declarations{$symbol};
951 if (defined ($declaration)) {
952 if (&CheckIsObject ($symbol)) {
953 push @file_objects, $symbol;
955 # We don't want standard macros/functions of GObjects,
956 # or private declarations.
957 if ($subsection ne "Standard" && $subsection ne "Private") {
958 my ($synop, $desc) = &OutputDeclaration ($symbol,
960 my $type = $DeclarationTypes {$symbol};
962 if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
963 $functions_synop .= $synop;
964 $functions_details .= $desc;
965 } elsif ($type eq 'MACRO' && $declaration =~ /$symbol\(/) {
966 $functions_synop .= $synop;
967 $functions_details .= $desc;
969 $other_synop .= $synop;
970 $other_details .= $desc;
973 my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
974 my ($arg_synop, $child_arg_synop, $style_arg_synop,
975 $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
976 my $ifaces = &GetInterfaces ($symbol);
977 my $impls = &GetImplementations ($symbol);
978 my $prereqs = &GetPrerequisites ($symbol);
979 my $der = &GetDerived ($symbol);
980 @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
982 $signals_synop .= $sig_synop;
983 $signals_desc .= $sig_desc;
984 $args_synop .= $arg_synop;
985 $child_args_synop .= $child_arg_synop;
986 $style_args_synop .= $style_arg_synop;
987 $args_desc .= $arg_desc;
988 $child_args_desc .= $child_arg_desc;
989 $style_args_desc .= $style_arg_desc;
990 $interfaces .= $ifaces;
991 $implementations .= $impls;
992 $prerequisites .= $prereqs;
995 # Note that the declaration has been output.
996 $DeclarationOutput{$symbol} = 1;
997 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
998 $UndeclaredSymbols{$symbol} = 1;
999 &LogWarning ($file, $., "No declaration found for $symbol.");
1002 $symbol_def_line{$symbol}=$.;
1004 if ($section_id eq "") {
1005 if($title eq "" && $filename eq "") {
1006 &LogWarning ($file, $., "Section has no title and no file.");
1008 # FIXME: one of those would be enough
1009 # filename should be an internal detail for gtk-doc
1012 } elsif ($filename eq "") {
1015 $filename =~ s/\s/_/g;
1017 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1018 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1019 # Remove trailing blanks and use as is
1020 $section_id =~ s/\s+$//;
1021 } elsif (&CheckIsObject ($title)) {
1022 # GObjects use their class name as the ID.
1023 $section_id = &CreateValidSGMLID ($title);
1025 $section_id = &CreateValidSGMLID ("$MODULE-$title");
1028 $SymbolSection{$symbol}=$title;
1029 $SymbolSectionId{$symbol}=$section_id;
1032 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1033 "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1039 &OutputMissingDocumentation;
1040 &OutputUndeclaredSymbols;
1041 &OutputUnusedSymbols;
1043 if ($OUTPUT_ALL_SYMBOLS) {
1046 if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1047 &OutputSymbolsWithoutSince;
1050 for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1051 my $file_changed = &OutputExtraFile ($filename);
1052 if ($file_changed) {
1057 &OutputBook ($book_top, $book_bottom);
1062 #############################################################################
1063 # Function : OutputIndex
1064 # Description : This writes an indexlist that can be included into the main-
1065 # document into an <index> tag.
1066 #############################################################################
1069 my ($basename, $apiindexref ) = @_;
1070 my %apiindex = %{$apiindexref};
1071 my $old_index = "$DB_OUTPUT_DIR/$basename.xml";
1072 my $new_index = "$DB_OUTPUT_DIR/$basename.new";
1073 my $lastletter = " ";
1078 open (OUTPUT, ">$new_index")
1079 || die "Can't create $new_index";
1081 print (OUTPUT &MakeDocHeader ("indexdiv")."\n<indexdiv id=\"$basename\">\n");
1083 @TRACE@("generate $basename index (".%apiindex." entries)\n");
1085 # do a case insensitive sort while chopping off the prefix
1087 sort { $$a{criteria} cmp $$b{criteria} or $$a{original} cmp $$b{original} }
1088 map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1091 $symbol = $$hash{original};
1092 if (defined($$hash{short}) && $$hash{short} ne "") {
1093 $short_symbol = $$hash{short};
1095 $short_symbol = $symbol;
1098 # generate a short symbol description
1099 my $symbol_desc = "";
1100 my $symbol_section = "";
1101 my $symbol_section_id = "";
1102 my $symbol_type = "";
1103 if (defined($DeclarationTypes{$symbol})) {
1104 $symbol_type = lc($DeclarationTypes{$symbol});
1106 if ($symbol_type eq "") {
1107 @TRACE@("trying symbol $symbol\n");
1108 if ($symbol =~ m/(.*)::(.*)/) {
1112 @TRACE@(" trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1113 for ($i = 0; $i <= $#SignalNames; $i++) {
1114 if ($SignalNames[$i] eq $osym) {
1115 $symbol_type = "object signal";
1116 if (defined($SymbolSection{$oname})) {
1117 $symbol_section = $SymbolSection{$oname};
1118 $symbol_section_id = $SymbolSectionId{$oname};
1123 } elsif ($symbol =~ m/(.*):(.*)/) {
1127 @TRACE@(" trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1128 for ($i = 0; $i <= $#ArgNames; $i++) {
1129 @TRACE@(" ".$ArgNames[$i]."\n");
1130 if ($ArgNames[$i] eq $osym) {
1131 $symbol_type = "object property";
1132 if (defined($SymbolSection{$oname})) {
1133 $symbol_section = $SymbolSection{$oname};
1134 $symbol_section_id = $SymbolSectionId{$oname};
1141 if (defined($SymbolSection{$symbol})) {
1142 $symbol_section = $SymbolSection{$symbol};
1143 $symbol_section_id = $SymbolSectionId{$symbol};
1146 if ($symbol_type ne "") {
1147 $symbol_desc=", $symbol_type";
1148 if ($symbol_section ne "") {
1149 $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1150 #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1154 my $curletter = uc(substr($short_symbol,0,1));
1155 my $id = $apiindex{$symbol};
1157 @TRACE@(" add symbol $symbol with $id to index in section '$curletter' (derived from $short_symbol)\n");
1159 if ($curletter ne $lastletter) {
1160 $lastletter = $curletter;
1162 if ($divopen == 1) {
1163 print (OUTPUT "</indexdiv>\n");
1165 print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1169 print (OUTPUT <<EOF);
1170 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1174 if ($divopen == 1) {
1175 print (OUTPUT "</indexdiv>\n");
1177 print (OUTPUT "</indexdiv>\n");
1180 &UpdateFileIfChanged ($old_index, $new_index, 0);
1184 #############################################################################
1185 # Function : OutputIndexFull
1186 # Description : This writes the full api indexlist that can be included into the
1187 # main document into an <index> tag.
1188 #############################################################################
1190 sub OutputIndexFull {
1191 &OutputIndex ("api-index-full", \%IndexEntriesFull);
1195 #############################################################################
1196 # Function : OutputDeprecatedIndex
1197 # Description : This writes the deprecated api indexlist that can be included
1198 # into the main document into an <index> tag.
1199 #############################################################################
1201 sub OutputDeprecatedIndex {
1202 &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1206 #############################################################################
1207 # Function : OutputSinceIndexes
1208 # Description : This writes the 'since' api indexlists that can be included into
1209 # the main document into an <index> tag.
1210 #############################################################################
1212 sub OutputSinceIndexes {
1213 my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1215 foreach my $version (@sinces) {
1216 @TRACE@("Since : [$version]\n");
1217 # TODO make filtered hash
1218 #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1219 my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1221 &OutputIndex ("api-index-$version", \%index);
1225 #############################################################################
1226 # Function : OutputAnnotationGlossary
1227 # Description : This writes a glossary of the used annotation terms into a
1228 # separate glossary file that can be included into the main
1230 #############################################################################
1232 sub OutputAnnotationGlossary {
1233 my $old_glossary = "$DB_OUTPUT_DIR/annotation-glossary.xml";
1234 my $new_glossary = "$DB_OUTPUT_DIR/annotation-glossary.new";
1235 my $lastletter = " ";
1238 # if there are no annotations used return
1239 return if (! keys(%AnnotationsUsed));
1241 # add acronyms that are referenced from acronym text
1243 foreach my $annotation (keys(%AnnotationsUsed)) {
1244 if(defined($AnnotationDefinition{$annotation})) {
1245 if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1246 if (!exists($AnnotationsUsed{$1})) {
1247 $AnnotationsUsed{$1} = 1;
1254 open (OUTPUT, ">$new_glossary")
1255 || die "Can't create $new_glossary";
1257 print (OUTPUT <<EOF);
1258 ${\( MakeDocHeader ("glossary") )}
1259 <glossary id="annotation-glossary">
1260 <title>Annotation Glossary</title>
1263 foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1264 if(defined($AnnotationDefinition{$annotation})) {
1265 my $def = $AnnotationDefinition{$annotation};
1266 my $curletter = uc(substr($annotation,0,1));
1268 if ($curletter ne $lastletter) {
1269 $lastletter = $curletter;
1271 if ($divopen == 1) {
1272 print (OUTPUT "</glossdiv>\n");
1274 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1277 print (OUTPUT <<EOF);
1279 <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1288 if ($divopen == 1) {
1289 print (OUTPUT "</glossdiv>\n");
1291 print (OUTPUT "</glossary>\n");
1294 &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1297 #############################################################################
1298 # Function : ReadKnownSymbols
1299 # Description : This collects the names of non-private symbols from the
1300 # $MODULE-sections.txt file.
1301 # Arguments : $file - the $MODULE-sections.txt file which contains all of
1302 # the functions/macros/structs etc. being documented, organised
1303 # into sections and subsections.
1304 #############################################################################
1306 sub ReadKnownSymbols {
1309 my $subsection = "";
1311 @TRACE@("Reading: $file\n");
1313 || die "Can't open $file: $!";
1319 } elsif (m/^<SECTION>/) {
1322 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1325 } elsif (m/^<SUBSECTION>/) {
1328 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1331 } elsif (m/^<FILE>(.*)<\/FILE>/) {
1332 $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1333 $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1336 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1339 } elsif (m/^<\/SECTION>/) {
1342 } elsif (m/^(\S+)/) {
1345 if ($subsection ne "Standard" && $subsection ne "Private") {
1346 $KnownSymbols{$symbol} = 1;
1349 $KnownSymbols{$symbol} = 0;
1357 #############################################################################
1358 # Function : OutputDeclaration
1359 # Description : Returns the synopsis and detailed description DocBook
1360 # describing one function/macro etc.
1361 # Arguments : $symbol - the name of the function/macro begin described.
1362 # $declaration - the declaration of the function/macro.
1363 #############################################################################
1365 sub OutputDeclaration {
1366 my ($symbol, $declaration) = @_;
1368 my $type = $DeclarationTypes {$symbol};
1369 if ($type eq 'MACRO') {
1370 return &OutputMacro ($symbol, $declaration);
1371 } elsif ($type eq 'TYPEDEF') {
1372 return &OutputTypedef ($symbol, $declaration);
1373 } elsif ($type eq 'STRUCT') {
1374 return &OutputStruct ($symbol, $declaration);
1375 } elsif ($type eq 'ENUM') {
1376 return &OutputEnum ($symbol, $declaration);
1377 } elsif ($type eq 'UNION') {
1378 return &OutputUnion ($symbol, $declaration);
1379 } elsif ($type eq 'VARIABLE') {
1380 return &OutputVariable ($symbol, $declaration);
1381 } elsif ($type eq 'FUNCTION') {
1382 return &OutputFunction ($symbol, $declaration, $type);
1383 } elsif ($type eq 'USER_FUNCTION') {
1384 return &OutputFunction ($symbol, $declaration, $type);
1386 die "Unknown symbol type";
1391 #############################################################################
1392 # Function : OutputSymbolTraits
1393 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1394 # Arguments : $symbol - the name of the function/macro begin described.
1395 #############################################################################
1397 sub OutputSymbolTraits {
1401 if (exists $Since{$symbol}) {
1402 my $link_id = "api-index-".$Since{$symbol};
1403 $desc .= "<para role=\"since\">Since: <link linkend=\"$link_id\">$Since{$symbol}</link></para>";
1405 if (exists $StabilityLevel{$symbol}) {
1406 my $stability = $StabilityLevel{$symbol};
1407 $AnnotationsUsed{$stability} = 1;
1408 $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1413 #############################################################################
1414 # Function : Output{Symbol,Section}ExtraLinks
1415 # Description : Returns extralinks for the symbol (if enabled).
1416 # Arguments : $symbol - the name of the function/macro begin described.
1417 #############################################################################
1421 return undef unless defined $text;
1423 # Build a char to hex map
1426 $escapes{chr($_)} = sprintf("%%%02X", $_);
1429 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1430 $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1435 sub OutputSymbolExtraLinks {
1439 if (0) { # NEW FEATURE: needs configurability
1440 my $sstr = &uri_escape($symbol);
1441 my $mstr = &uri_escape($MODULE);
1443 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1444 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$sstr">edit documentation</ulink>
1450 sub OutputSectionExtraLinks {
1451 my ($symbol,$docsymbol) = @_;
1454 if (0) { # NEW FEATURE: needs configurability
1455 my $sstr = &uri_escape($symbol);
1456 my $mstr = &uri_escape($MODULE);
1457 my $dsstr = &uri_escape($docsymbol);
1459 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1460 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$dsstr">edit documentation</ulink>
1467 #############################################################################
1468 # Function : OutputMacro
1469 # Description : Returns the synopsis and detailed description of a macro.
1470 # Arguments : $symbol - the macro.
1471 # $declaration - the declaration of the macro.
1472 #############################################################################
1475 my ($symbol, $declaration) = @_;
1476 my $id = &CreateValidSGMLID ($symbol);
1477 my $condition = &MakeConditionDescription ($symbol);
1478 my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1481 my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1482 my $title = $symbol . (@fields ? "()" : "");
1484 $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1485 $desc .= MakeIndexterms($symbol, $id);
1487 $desc .= OutputSymbolExtraLinks($symbol);
1490 $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1492 $synop .= "</entry></row>\n";
1494 # Don't output the macro definition if is is a conditional macro or it
1495 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1496 # longer than 2 lines, otherwise we get lots of complicated macros like
1498 if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1499 && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1500 my $decl_out = &CreateValidSGML ($declaration);
1501 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1503 $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1504 if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1506 my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1507 # Align each line so that if should all line up OK.
1508 $args =~ s/\n/\n$pad/gm;
1509 $desc .= &CreateValidSGML ($args);
1511 $desc .= "</programlisting>\n";
1514 $desc .= &MakeDeprecationNote($symbol);
1516 my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1518 if (defined ($SymbolDocs{$symbol})) {
1519 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1520 $desc .= $symbol_docs;
1523 $desc .= $parameters;
1524 $desc .= OutputSymbolTraits ($symbol);
1525 $desc .= "</refsect2>\n";
1526 return ($synop, $desc);
1530 #############################################################################
1531 # Function : OutputTypedef
1532 # Description : Returns the synopsis and detailed description of a typedef.
1533 # Arguments : $symbol - the typedef.
1534 # $declaration - the declaration of the typedef,
1535 # e.g. 'typedef unsigned int guint;'
1536 #############################################################################
1539 my ($symbol, $declaration) = @_;
1540 my $id = &CreateValidSGMLID ($symbol);
1541 my $condition = &MakeConditionDescription ($symbol);
1542 my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1543 my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1545 $desc .= MakeIndexterms($symbol, $id);
1547 $desc .= OutputSymbolExtraLinks($symbol);
1549 if (!defined ($DeclarationConditional{$symbol})) {
1550 my $decl_out = &CreateValidSGML ($declaration);
1551 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1554 $desc .= &MakeDeprecationNote($symbol);
1556 if (defined ($SymbolDocs{$symbol})) {
1557 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1559 $desc .= OutputSymbolTraits ($symbol);
1560 $desc .= "</refsect2>\n";
1561 return ($synop, $desc);
1565 #############################################################################
1566 # Function : OutputStruct
1567 # Description : Returns the synopsis and detailed description of a struct.
1568 # We check if it is a object struct, and if so we only output
1569 # parts of it that are noted as public fields.
1570 # We also use a different IDs for object structs, since the
1571 # original ID is used for the entire RefEntry.
1572 # Arguments : $symbol - the struct.
1573 # $declaration - the declaration of the struct.
1574 #############################################################################
1577 my ($symbol, $declaration) = @_;
1580 my $default_to_public = 1;
1581 if (&CheckIsObject ($symbol)) {
1582 @TRACE@("Found struct gtype: $symbol\n");
1584 $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1590 $id = &CreateValidSGMLID ($symbol . "_struct");
1591 $condition = &MakeConditionDescription ($symbol . "_struct");
1593 $id = &CreateValidSGMLID ($symbol);
1594 $condition = &MakeConditionDescription ($symbol);
1597 # Determine if it is a simple struct or it also has a typedef.
1598 my $has_typedef = 0;
1599 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1606 # For structs with typedefs we just output the struct name.
1608 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1610 $type_output = "struct";
1611 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1613 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1615 $desc .= MakeIndexterms($symbol, $id);
1617 $desc .= OutputSymbolExtraLinks($symbol);
1619 # Form a pretty-printed, private-data-removed form of the declaration
1622 if ($declaration =~ m/^\s*$/) {
1623 @TRACE@("Found opaque struct: $symbol\n");
1624 $decl_out = "typedef struct _$symbol $symbol;";
1625 } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1626 @TRACE@("Found opaque struct: $symbol\n");
1627 $decl_out = "struct $symbol;";
1629 my $public = $default_to_public;
1630 my $new_declaration = "";
1632 my $decl = $declaration;
1634 if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1635 my $struct_contents = $2;
1637 foreach $decl_line (split (/\n/, $struct_contents)) {
1638 @TRACE@("Struct line: $decl_line\n");
1639 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1641 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1644 $new_declaration .= $decl_line . "\n";
1648 if ($new_declaration) {
1649 # Strip any blank lines off the ends.
1650 $new_declaration =~ s/^\s*\n//;
1651 $new_declaration =~ s/\n\s*$/\n/;
1654 $decl_out = "typedef struct {\n" . $new_declaration
1657 $decl_out = "struct $symbol {\n" . $new_declaration
1662 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1663 "Couldn't parse struct:\n$declaration");
1666 # If we couldn't parse the struct or it was all private, output an
1667 # empty struct declaration.
1668 if ($decl_out eq "") {
1670 $decl_out = "typedef struct _$symbol $symbol;";
1672 $decl_out = "struct $symbol;";
1677 $decl_out = &CreateValidSGML ($decl_out);
1678 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1680 $desc .= &MakeDeprecationNote($symbol);
1682 if (defined ($SymbolDocs{$symbol})) {
1683 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1686 # Create a table of fields and descriptions
1688 # FIXME: Inserting  's into the produced type declarations here would
1689 # improve the output in most situations ... except for function
1690 # members of structs!
1691 my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1694 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1696 my $params = $SymbolParams{$symbol};
1698 # If no parameters are filled in, we don't generate the description
1699 # table, for backwards compatibility.
1702 if (defined $params) {
1703 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1704 if ($params->[$i] =~ /\S/) {
1712 my %field_descrs = @$params;
1713 my $missing_parameters = "";
1714 my $unused_parameters = "";
1715 my $id = &CreateValidSGMLID ("$symbol".".members");
1718 <refsect3 id="$id" role="struct_members">\n<title>Members</title>
1719 <informaltable role="struct_members_table" pgwide="1" frame="none">
1721 <colspec colname="struct_members_name" colwidth="300px"/>
1722 <colspec colname="struct_members_description"/>
1723 <colspec colname="struct_members_annotations" colwidth="200px"/>
1728 my $field_name = shift @fields;
1729 my $text = shift @fields;
1730 my $field_descr = $field_descrs{$field_name};
1731 my $param_annotations = "";
1733 $desc .= "<row role=\"member\"><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1734 if (defined $field_descr) {
1735 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1736 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1738 $field_descr =~ s/^(\s|\n)+//msg;
1739 $field_descr =~ s/(\s|\n)+$//msg;
1740 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1741 delete $field_descrs{$field_name};
1743 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1744 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1745 if ($missing_parameters ne "") {
1746 $missing_parameters .= ", ".$field_name;
1748 $missing_parameters = $field_name;
1750 $desc .= "<entry /><entry />\n";
1752 $desc .= "</row>\n";
1754 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1755 foreach my $field_name (keys %field_descrs) {
1756 # Documenting those standard fields is not required anymore, but
1757 # we don't want to warn if they are documented anyway.
1758 if ($field_name =~ /(g_iface|parent_instance|parent_class)/) {
1761 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1762 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1763 if ($unused_parameters ne "") {
1764 $unused_parameters .= ", ".$field_name;
1766 $unused_parameters = $field_name;
1770 # remember missing/unused parameters (needed in tmpl-free build)
1771 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1772 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1774 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1775 $AllUnusedSymbols{$symbol}=$unused_parameters;
1779 if (scalar(@fields) > 0) {
1780 if (! exists ($AllIncompleteSymbols{$symbol})) {
1781 $AllIncompleteSymbols{$symbol}="<items>";
1782 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1783 "Field descriptions for struct $symbol are missing in source code comment block.");
1784 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1789 $desc .= OutputSymbolTraits ($symbol);
1790 $desc .= "</refsect2>\n";
1791 return ($synop, $desc);
1795 #############################################################################
1796 # Function : OutputUnion
1797 # Description : Returns the synopsis and detailed description of a union.
1798 # Arguments : $symbol - the union.
1799 # $declaration - the declaration of the union.
1800 #############################################################################
1803 my ($symbol, $declaration) = @_;
1806 if (&CheckIsObject ($symbol)) {
1807 @TRACE@("Found union gtype: $symbol\n");
1814 $id = &CreateValidSGMLID ($symbol . "_union");
1815 $condition = &MakeConditionDescription ($symbol . "_union");
1817 $id = &CreateValidSGMLID ($symbol);
1818 $condition = &MakeConditionDescription ($symbol);
1821 # Determine if it is a simple struct or it also has a typedef.
1822 my $has_typedef = 0;
1823 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1830 # For unions with typedefs we just output the union name.
1832 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1834 $type_output = "union";
1835 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1837 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1839 $desc .= MakeIndexterms($symbol, $id);
1841 $desc .= OutputSymbolExtraLinks($symbol);
1842 $desc .= &MakeDeprecationNote($symbol);
1844 if (defined ($SymbolDocs{$symbol})) {
1845 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1848 # Create a table of fields and descriptions
1850 # FIXME: Inserting  's into the produced type declarations here would
1851 # improve the output in most situations ... except for function
1852 # members of structs!
1853 my @fields = ParseStructDeclaration($declaration, 0,
1856 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1858 my $params = $SymbolParams{$symbol};
1860 # If no parameters are filled in, we don't generate the description
1861 # table, for backwards compatibility
1864 if (defined $params) {
1865 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1866 if ($params->[$i] =~ /\S/) {
1874 my %field_descrs = @$params;
1875 my $missing_parameters = "";
1876 my $unused_parameters = "";
1877 my $id = &CreateValidSGMLID ("$symbol".".members");
1880 <refsect3 id="$id" role="union_members">\n<title>Members</title>
1881 <informaltable role="union_members_table" pgwide="1" frame="none">
1883 <colspec colname="union_members_name" colwidth="300px"/>
1884 <colspec colname="union_members_description"/>
1885 <colspec colname="union_members_annotations" colwidth="200px"/>
1890 my $field_name = shift @fields;
1891 my $text = shift @fields;
1892 my $field_descr = $field_descrs{$field_name};
1893 my $param_annotations = "";
1895 $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1896 if (defined $field_descr) {
1897 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1898 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1901 $field_descr =~ s/^(\s|\n)+//msg;
1902 $field_descr =~ s/(\s|\n)+$//msg;
1903 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1904 delete $field_descrs{$field_name};
1906 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1907 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1908 if ($missing_parameters ne "") {
1909 $missing_parameters .= ", ".$field_name;
1911 $missing_parameters = $field_name;
1913 $desc .= "<entry /><entry />\n";
1915 $desc .= "</row>\n";
1917 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1918 foreach my $field_name (keys %field_descrs) {
1919 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1920 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1921 if ($unused_parameters ne "") {
1922 $unused_parameters .= ", ".$field_name;
1924 $unused_parameters = $field_name;
1928 # remember missing/unused parameters (needed in tmpl-free build)
1929 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1930 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1932 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1933 $AllUnusedSymbols{$symbol}=$unused_parameters;
1937 if (scalar(@fields) > 0) {
1938 if (! exists ($AllIncompleteSymbols{$symbol})) {
1939 $AllIncompleteSymbols{$symbol}="<items>";
1940 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1941 "Field descriptions for union $symbol are missing in source code comment block.");
1942 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1947 $desc .= OutputSymbolTraits ($symbol);
1948 $desc .= "</refsect2>\n";
1949 return ($synop, $desc);
1953 #############################################################################
1954 # Function : OutputEnum
1955 # Description : Returns the synopsis and detailed description of a enum.
1956 # Arguments : $symbol - the enum.
1957 # $declaration - the declaration of the enum.
1958 #############################################################################
1961 my ($symbol, $declaration) = @_;
1964 if (&CheckIsObject ($symbol)) {
1965 @TRACE@("Found enum gtype: $symbol\n");
1972 $id = &CreateValidSGMLID ($symbol . "_enum");
1973 $condition = &MakeConditionDescription ($symbol . "_enum");
1975 $id = &CreateValidSGMLID ($symbol);
1976 $condition = &MakeConditionDescription ($symbol);
1979 my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1980 my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1982 $desc .= MakeIndexterms($symbol, $id);
1984 $desc .= OutputSymbolExtraLinks($symbol);
1985 $desc .= &MakeDeprecationNote($symbol);
1987 if (defined ($SymbolDocs{$symbol})) {
1988 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1991 # Create a table of fields and descriptions
1993 my @fields = ParseEnumDeclaration($declaration);
1994 my $params = $SymbolParams{$symbol};
1996 # If nothing at all is documented log a single summary warning at the end.
1997 # Otherwise, warn about each undocumented item.
2000 if (defined $params) {
2001 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
2002 if ($params->[$i] =~ /\S/) {
2009 my %field_descrs = (defined $params ? @$params : ());
2010 my $missing_parameters = "";
2011 my $unused_parameters = "";
2013 $id = &CreateValidSGMLID ("$symbol".".members");
2015 <refsect3 id="$id" role="enum_members">\n<title>Members</title>
2016 <informaltable role="enum_members_table" pgwide="1" frame="none">
2018 <colspec colname="enum_members_name" colwidth="300px"/>
2019 <colspec colname="enum_members_description"/>
2020 <colspec colname="enum_members_annotations" colwidth="200px"/>
2024 for my $field_name (@fields) {
2025 my $field_descr = $field_descrs{$field_name};
2026 my $param_annotations = "";
2028 $id = &CreateValidSGMLID ($field_name);
2029 $condition = &MakeConditionDescription ($field_name);
2030 $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2031 if (defined $field_descr) {
2032 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2033 $field_descr = &ConvertMarkDown($symbol, $field_descr);
2034 $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2035 delete $field_descrs{$field_name};
2038 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2039 "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2040 if ($missing_parameters ne "") {
2041 $missing_parameters .= ", ".$field_name;
2043 $missing_parameters = $field_name;
2046 $desc .= "<entry /><entry />\n";
2048 $desc .= "</row>\n";
2050 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2051 foreach my $field_name (keys %field_descrs) {
2052 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2053 "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2054 if ($unused_parameters ne "") {
2055 $unused_parameters .= ", ".$field_name;
2057 $unused_parameters = $field_name;
2061 # remember missing/unused parameters (needed in tmpl-free build)
2062 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2063 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2065 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2066 $AllUnusedSymbols{$symbol}=$unused_parameters;
2070 if (scalar(@fields) > 0) {
2071 if (! exists ($AllIncompleteSymbols{$symbol})) {
2072 $AllIncompleteSymbols{$symbol}="<items>";
2073 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2074 "Value descriptions for $symbol are missing in source code comment block.");
2079 $desc .= OutputSymbolTraits ($symbol);
2080 $desc .= "</refsect2>\n";
2081 return ($synop, $desc);
2085 #############################################################################
2086 # Function : OutputVariable
2087 # Description : Returns the synopsis and detailed description of a variable.
2088 # Arguments : $symbol - the extern'ed variable.
2089 # $declaration - the declaration of the variable.
2090 #############################################################################
2092 sub OutputVariable {
2093 my ($symbol, $declaration) = @_;
2094 my $id = &CreateValidSGMLID ($symbol);
2095 my $condition = &MakeConditionDescription ($symbol);
2097 @TRACE@("ouputing variable: '$symbol' '$declaration'");
2100 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*;/) {
2101 my $mod1 = defined ($1) ? $1 : "";
2102 my $ptr = defined ($3) ? $3 : "";
2103 my $space = defined ($4) ? $4 : "";
2104 my $mod2 = defined ($5) ? $5 : "";
2105 $type_output = "extern $mod1$ptr$space$mod2";
2106 } 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*=/) {
2107 my $mod1 = defined ($1) ? $1 : "";
2108 my $ptr = defined ($3) ? $3 : "";
2109 my $space = defined ($4) ? $4 : "";
2110 my $mod2 = defined ($5) ? $5 : "";
2111 $type_output = "$mod1$ptr$space$mod2";
2113 $type_output = "extern";
2115 my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2117 my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2119 $desc .= MakeIndexterms($symbol, $id);
2121 $desc .= OutputSymbolExtraLinks($symbol);
2123 my $decl_out = &CreateValidSGML ($declaration);
2124 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2126 $desc .= &MakeDeprecationNote($symbol);
2128 if (defined ($SymbolDocs{$symbol})) {
2129 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2131 if (defined ($SymbolAnnotations{$symbol})) {
2132 my $param_desc = $SymbolAnnotations{$symbol};
2133 my $param_annotations = "";
2134 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2135 if ($param_annotations ne "") {
2136 $desc .= "\n<para>$param_annotations</para>";
2140 $desc .= OutputSymbolTraits ($symbol);
2141 $desc .= "</refsect2>\n";
2142 return ($synop, $desc);
2146 #############################################################################
2147 # Function : OutputFunction
2148 # Description : Returns the synopsis and detailed description of a function.
2149 # Arguments : $symbol - the function.
2150 # $declaration - the declaration of the function.
2151 #############################################################################
2153 sub OutputFunction {
2154 my ($symbol, $declaration, $symbol_type) = @_;
2155 my $id = &CreateValidSGMLID ($symbol);
2156 my $condition = &MakeConditionDescription ($symbol);
2158 # Take out the return type $1 $2 $3
2159 $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//;
2160 my $type_modifier = defined($1) ? $1 : "";
2163 # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2164 $pointer =~ s/\s+$//;
2165 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2167 #if ($symbol_type eq 'USER_FUNCTION') {
2168 # $start = "typedef ";
2171 # We output const rather than G_CONST_RETURN.
2172 $type_modifier =~ s/G_CONST_RETURN/const/g;
2173 $pointer =~ s/G_CONST_RETURN/const/g;
2174 $pointer =~ s/^\s+/ /g;
2176 my $ret_type_output;
2177 $ret_type_output = "$start$type_modifier$xref$pointer\n";
2180 $indent_len = length ($symbol) + 2;
2181 my $char1 = my $char2 = my $char3 = "";
2182 if ($symbol_type eq 'USER_FUNCTION') {
2184 $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2186 $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2189 my ($symbol_output, $symbol_desc_output);
2190 $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2191 if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2192 $symbol_desc_output = "$char1$char2$symbol$char3 ";
2194 $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2195 $symbol_desc_output = "$char1$char2$symbol$char3\n"
2196 . (' ' x ($indent_len - 1));
2199 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";
2201 my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol} ()</title>\n";
2203 $desc .= MakeIndexterms($symbol, $id);
2205 $desc .= OutputSymbolExtraLinks($symbol);
2207 $desc .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2209 my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2211 &tagify($_[0],"parameter");
2214 for (my $i = 1; $i <= $#fields; $i += 2) {
2215 my $field_name = $fields[$i];
2218 $desc .= "$field_name";
2221 . (' ' x $indent_len)
2227 $desc .= ");</programlisting>\n";
2229 $desc .= &MakeDeprecationNote($symbol);
2231 if (defined ($SymbolDocs{$symbol})) {
2232 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2234 if (defined ($SymbolAnnotations{$symbol})) {
2235 my $param_desc = $SymbolAnnotations{$symbol};
2236 my $param_annotations = "";
2237 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2238 if ($param_annotations ne "") {
2239 $desc .= "\n<para>$param_annotations</para>";
2243 $desc .= &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2244 $desc .= OutputSymbolTraits ($symbol);
2245 $desc .= "</refsect2>\n";
2246 return ($synop, $desc);
2250 #############################################################################
2251 # Function : OutputParamDescriptions
2252 # Description : Returns the DocBook output describing the parameters of a
2253 # function, macro or signal handler.
2254 # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2255 # handlers have an implicit user_data parameter last.
2256 # $symbol - the name of the function/macro being described.
2257 # @fields - parsed fields from the declaration, used to determine
2258 # undocumented/unused entries
2259 #############################################################################
2261 sub OutputParamDescriptions {
2262 my ($symbol_type, $symbol, @fields) = @_;
2264 my $params = $SymbolParams{$symbol};
2266 my %field_descrs = ();
2269 %field_descrs = @fields;
2270 delete $field_descrs{"void"};
2271 delete $field_descrs{"Returns"};
2274 if (defined $params) {
2276 my $params_desc = "";
2277 my $missing_parameters = "";
2278 my $unused_parameters = "";
2281 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2282 my $param_name = $$params[$j];
2283 my $param_desc = $$params[$j + 1];
2284 my $param_annotations = "";
2286 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2287 $param_desc = &ConvertMarkDown($symbol, $param_desc);
2289 $param_desc =~ s/^(\s|\n)+//msg;
2290 $param_desc =~ s/(\s|\n)+$//msg;
2291 if ($param_name eq "Returns") {
2292 $returns = $param_desc;
2293 if ($param_annotations ne "") {
2294 $returns .= "\n<para>$param_annotations</para>";
2296 } elsif ($param_name eq "void") {
2297 # FIXME: &LogWarning()?
2298 @TRACE@("!!!! void in params for $symbol?\n");
2301 if (!defined $field_descrs{$param_name}) {
2302 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2303 "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2304 if ($unused_parameters ne "") {
2305 $unused_parameters .= ", ".$param_name;
2307 $unused_parameters = $param_name;
2310 delete $field_descrs{$param_name};
2313 if($param_desc ne "") {
2314 $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";
2319 foreach my $param_name (keys %field_descrs) {
2320 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2321 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2322 if ($missing_parameters ne "") {
2323 $missing_parameters .= ", ".$param_name;
2325 $missing_parameters = $param_name;
2329 # Signals have an implicit user_data parameter which we describe.
2330 if ($symbol_type eq "SIGNAL") {
2331 $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";
2334 # Start a table if we need one.
2335 if ($params_desc ne "") {
2336 my $id = &CreateValidSGMLID ("$symbol".".parameters");
2339 <refsect3 id="$id" role="parameters">\n<title>Parameters</title>
2340 <informaltable role="parameters_table" pgwide="1" frame="none">
2342 <colspec colname="parameters_name" colwidth="150px"/>
2343 <colspec colname="parameters_description"/>
2344 <colspec colname="parameters_annotations" colwidth="200px"/>
2347 $output .= $params_desc;
2348 $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2351 # Output the returns info last
2352 if ($returns ne "") {
2353 my $id = &CreateValidSGMLID ("$symbol".".returns");
2356 <refsect3 id="$id" role=\"returns\">\n<title>Returns</title>
2358 $output .= $returns;
2359 $output .= "\n</refsect3>";
2362 # remember missing/unused parameters (needed in tmpl-free build)
2363 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2364 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2366 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2367 $AllUnusedSymbols{$symbol}=$unused_parameters;
2370 if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2371 if (! exists ($AllIncompleteSymbols{$symbol})) {
2372 $AllIncompleteSymbols{$symbol}="<parameters>";
2380 #############################################################################
2381 # Function : ParseStabilityLevel
2382 # Description : Parses a stability level and outputs a warning if it isn't
2384 # Arguments : $stability - the stability text.
2385 # $file, $line - context for error message
2386 # $message - description of where the level is from, to use in
2387 # any error message.
2388 # Returns : The parsed stability level string.
2389 #############################################################################
2391 sub ParseStabilityLevel {
2392 my ($stability, $file, $line, $message) = @_;
2394 $stability =~ s/^\s*//;
2395 $stability =~ s/\s*$//;
2396 if ($stability =~ m/^stable$/i) {
2397 $stability = "Stable";
2398 } elsif ($stability =~ m/^unstable$/i) {
2399 $stability = "Unstable";
2400 } elsif ($stability =~ m/^private$/i) {
2401 $stability = "Private";
2403 &LogWarning ($file, $line, "$message is $stability.".
2404 "It should be one of these: Stable, Unstable, or Private.");
2410 #############################################################################
2411 # Function : OutputDBFile
2412 # Description : Outputs the final DocBook file for one section.
2413 # Arguments : $file - the name of the file.
2414 # $title - the title from the $MODULE-sections.txt file, which
2415 # will be overridden by the title in the template file.
2416 # $section_id - the id to use for the toplevel tag.
2417 # $includes - comma-separates list of include files added at top of
2418 # synopsis, with '<' '>' around them (if not already enclosed in "").
2419 # $functions_synop - reference to the DocBook for the Functions Synopsis part.
2420 # $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2421 # $functions_details - reference to the DocBook for the Functions Details part.
2422 # $other_details - reference to the DocBook for the Types and Values Details part.
2423 # $signal_synop - reference to the DocBook for the Signal Synopsis part
2424 # $signal_desc - reference to the DocBook for the Signal Description part
2425 # $args_synop - reference to the DocBook for the Arg Synopsis part
2426 # $args_desc - reference to the DocBook for the Arg Description part
2427 # $hierarchy - reference to the DocBook for the Object Hierarchy part
2428 # $interfaces - reference to the DocBook for the Interfaces part
2429 # $implementations - reference to the DocBook for the Known Implementations part
2430 # $prerequisites - reference to the DocBook for the Prerequisites part
2431 # $derived - reference to the DocBook for the Derived Interfaces part
2432 # $file_objects - reference to an array of objects in this file
2433 #############################################################################
2436 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) = @_;
2438 @TRACE@("Output docbook for file $file with title '$title'\n");
2440 # The edited title overrides the one from the sections file.
2441 my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2442 if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2443 $title = $new_title;
2444 @TRACE@("Found title: $title\n");
2446 my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2447 if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2450 # Don't use ConvertMarkDown here for now since we don't want blocks
2451 $short_desc = &ExpandAbbreviations("$title:Short_description",
2453 @TRACE@("Found short_desc: $short_desc");
2455 my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2456 if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2459 $long_desc = &ConvertMarkDown("$title:Long_description",
2461 @TRACE@("Found long_desc: $long_desc");
2463 my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2464 if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2467 $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2468 @TRACE@("Found see_also: $see_also");
2471 $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2473 my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2474 if (!defined ($stability) || $stability =~ m/^\s*$/) {
2477 $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2478 @TRACE@("Found stability: $stability");
2481 $AnnotationsUsed{$stability} = 1;
2482 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2483 } elsif ($DEFAULT_STABILITY) {
2484 $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2485 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2488 my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2489 if (!defined ($image) || $image =~ m/^\s*$/) {
2497 if ($image =~ /jpe?g$/i) {
2498 $format = "format='JPEG'";
2499 } elsif ($image =~ /png$/i) {
2500 $format = "format='PNG'";
2501 } elsif ($image =~ /svg$/i) {
2502 $format = "format='SVG'";
2507 $image = " <inlinegraphic fileref='$image' $format/>\n"
2510 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2512 my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2515 my $include_output = "";
2517 $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2519 foreach $include (split (/,/, $includes)) {
2520 if ($include =~ m/^\".+\"$/) {
2521 $include_output .= "#include ${include}\n";
2524 $include =~ s/^\s+|\s+$//gs;
2525 $include_output .= "#include <${include}>\n";
2528 $include_output .= "</synopsis></refsect1>\n";
2531 my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2533 my $old_db_file = "$DB_OUTPUT_DIR/$file.xml";
2534 my $new_db_file = "$DB_OUTPUT_DIR/$file.xml.new";
2536 open (OUTPUT, ">$new_db_file")
2537 || die "Can't create $new_db_file: $!";
2539 my $object_anchors = "";
2540 foreach my $object (@$file_objects) {
2541 next if ($object eq $section_id);
2542 my $id = CreateValidSGMLID($object);
2543 @TRACE@("Adding anchor for $object\n");
2544 $object_anchors .= "<anchor id=\"$id\"/>";
2547 # Make sure we produce valid docbook
2548 $$functions_details ||= "<para />";
2550 # We used to output this, but is messes up our UpdateFileIfChanged code
2551 # since it changes every day (and it is only used in the man pages):
2552 # "<refentry id="$section_id" revision="$mday $month $year">"
2555 ${\( MakeDocHeader ("refentry") )}
2556 <refentry id="$section_id">
2558 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2559 <manvolnum>3</manvolnum>
2560 <refmiscinfo>\U$MODULE\E Library$image</refmiscinfo>
2563 <refname>$title</refname>
2564 <refpurpose>$short_desc</refpurpose>
2567 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2569 <refsect1 id="$section_id.description" role="desc">
2570 <title role="desc.title">Description</title>
2571 $extralinks$long_desc
2573 <refsect1 id="$section_id.functions_details" role="details">
2574 <title role="details.title">Functions</title>
2577 <refsect1 id="$section_id.other_details" role="details">
2578 <title role="details.title">Types and Values</title>
2581 $$args_desc$$signals_desc$see_also
2586 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2589 #############################################################################
2590 # Function : OutputProgramDBFile
2591 # Description : Outputs the final DocBook file for one program.
2592 # Arguments : $file - the name of the file.
2593 # $section_id - the id to use for the toplevel tag.
2594 #############################################################################
2596 sub OutputProgramDBFile {
2597 my ($program, $section_id) = @_;
2599 @TRACE@("Output program docbook for $program\n");
2601 my $short_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Short_Description"};
2602 if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2605 # Don't use ConvertMarkDown here for now since we don't want blocks
2606 $short_desc = &ExpandAbbreviations("$program", $short_desc);
2607 @TRACE@("Found short_desc: $short_desc");
2610 my $synopsis = $SourceSymbolDocs{"$TMPL_DIR/$program:Synopsis"};
2611 if (defined ($synopsis) && $synopsis !~ m/^\s*$/) {
2613 my @items = split(' ', $synopsis);
2614 for ($i = 0; $i <= $#items; $i++) {
2615 my $parameter = $items[$i];
2616 my $choice = "plain";
2619 # first parameter is the command name
2621 $synopsis = "<command>$parameter</command>\n";
2625 # square brackets indicate optional parameters, curly brackets
2626 # indicate required parameters ("plain" parameters are also
2627 # mandatory, but do not get extra decoration)
2628 if ($parameter =~ s/^\[(.+?)\]$/$1/) {
2630 } elsif ($parameter =~ s/^\{(.+?)\}$/$1/) {
2634 # parameters ending in "..." are repeatable
2635 if ($parameter =~ s/\.\.\.$//) {
2636 $rep = " rep=\"repeat\"";
2639 # italic parameters are replaceable parameters
2640 if ($parameter =~ s/\*(.+?)\*/$1/) {
2641 $parameter = "<replaceable>$parameter</replaceable>";
2644 $synopsis .= "<arg choice=\"$choice\"$rep>";
2645 $synopsis .= $parameter;
2646 $synopsis .= "</arg>\n";
2649 @TRACE@("Found synopsis: $synopsis");
2651 $synopsis = "<command>$program</command>";
2654 my $long_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Long_Description"};
2655 if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2658 $long_desc = &ConvertMarkDown("$program:Long_description", $long_desc);
2659 @TRACE@("Found long_desc: $long_desc");
2663 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$program:Options"})) {
2664 my @opts = @{ $SourceSymbolDocs{"$TMPL_DIR/$program:Options"} };
2667 $options = "<refsect1>\n<title>Options</title>\n<variablelist>\n";
2668 for ($k = 0; $k <= $#opts; $k += 2) {
2669 my $opt_desc = $opts[$k+1];
2673 $opt_desc =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
2675 $options .= "<varlistentry>\n<term>";
2676 @opt_names = split (',', $opts[$k]);
2677 for ($i = 0; $i <= $#opt_names; $i++) {
2678 my $prefix = ($i > 0) ? ", " : "";
2679 $opt_names[$i] =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
2681 $options .= "$prefix<option>$opt_names[$i]</option>\n";
2683 $options .= "</term>\n";
2684 $options .= "<listitem><para>$opt_desc</para></listitem>\n";
2685 $options .= "</varlistentry>\n";
2687 $options .= "</variablelist></refsect1>\n";
2690 my $exit_status = $SourceSymbolDocs{"$TMPL_DIR/$program:Returns"};
2691 if (defined ($exit_status) && $exit_status ne "") {
2692 $exit_status = &ConvertMarkDown("$program:Returns", $exit_status);
2693 $exit_status = "<refsect1 id=\"$section_id.exit-status\">\n<title>Exit Status</title>\n$exit_status\n</refsect1>\n";
2698 my $see_also = $SourceSymbolDocs{"$TMPL_DIR/$program:See_Also"};
2699 if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2702 $see_also = &ConvertMarkDown("$program:See_Also", $see_also);
2703 @TRACE@("Found see_also: $see_also");
2706 $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2709 my $old_db_file = "$DB_OUTPUT_DIR/$program.xml";
2710 my $new_db_file = "$DB_OUTPUT_DIR/$program.xml.new";
2712 open (OUTPUT, ">$new_db_file")
2713 || die "Can't create $new_db_file: $!";
2716 ${\( MakeDocHeader ("refentry") )}
2717 <refentry id="$section_id">
2719 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$program</refentrytitle>
2720 <manvolnum>1</manvolnum>
2721 <refmiscinfo>User Commands</refmiscinfo>
2724 <refname>$program</refname>
2725 <refpurpose>$short_desc</refpurpose>
2728 <cmdsynopsis>$synopsis</cmdsynopsis>
2730 <refsect1 id="$section_id.description" role="desc">
2731 <title role="desc.title">Description</title>
2734 $options$exit_status$see_also
2739 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2744 #############################################################################
2745 # Function : OutputExtraFile
2746 # Description : Copies an "extra" DocBook file into the output directory,
2747 # expanding abbreviations
2748 # Arguments : $file - the source file.
2749 #############################################################################
2750 sub OutputExtraFile {
2755 ($basename = $file) =~ s!^.*/!!;
2757 my $old_db_file = "$DB_OUTPUT_DIR/$basename";
2758 my $new_db_file = "$DB_OUTPUT_DIR/$basename.new";
2762 open(EXTRA_FILE, "<$file") || die "Can't open $file";
2766 $contents = <EXTRA_FILE>;
2769 open (OUTPUT, ">$new_db_file")
2770 || die "Can't create $new_db_file: $!";
2772 print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2775 return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2777 #############################################################################
2778 # Function : OutputBook
2779 # Description : Outputs the entities that need to be included into the
2780 # main docbook file for the module.
2781 # Arguments : $book_top - the declarations of the entities, which are added
2782 # at the top of the main docbook file.
2783 # $book_bottom - the references to the entities, which are
2784 # added in the main docbook file at the desired position.
2785 #############################################################################
2788 my ($book_top, $book_bottom) = @_;
2790 my $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.top";
2791 my $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.top.new";
2793 open (OUTPUT, ">$new_file")
2794 || die "Can't create $new_file: $!";
2795 print OUTPUT $book_top;
2798 &UpdateFileIfChanged ($old_file, $new_file, 0);
2801 $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom";
2802 $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom.new";
2804 open (OUTPUT, ">$new_file")
2805 || die "Can't create $new_file: $!";
2806 print OUTPUT $book_bottom;
2809 &UpdateFileIfChanged ($old_file, $new_file, 0);
2812 # If the main docbook file hasn't been created yet, we create it here.
2813 # The user can tweak it later.
2814 if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2815 open (OUTPUT, ">$MAIN_SGML_FILE")
2816 || die "Can't create $MAIN_SGML_FILE: $!";
2819 ${\( MakeDocHeader ("book") )}
2822 <title>&package_name; Reference Manual</title>
2824 for &package_string;.
2825 The latest version of this documentation can be found on-line at
2826 <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2831 <title>[Insert title here]</title>
2835 if (-e $OBJECT_TREE_FILE) {
2837 <chapter id="object-tree">
2838 <title>Object Hierarchy</title>
2839 <xi:include href="xml/tree_index.sgml"/>
2844 <!-- enable this when you use gobject types
2845 <chapter id="object-tree">
2846 <title>Object Hierarchy</title>
2847 <xi:include href="xml/tree_index.sgml"/>
2853 <index id="api-index-full">
2854 <title>API Index</title>
2855 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2857 <index id="deprecated-api-index" role="deprecated">
2858 <title>Index of deprecated API</title>
2859 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2862 if (keys(%AnnotationsUsed)) {
2864 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2868 <!-- enable this when you use gobject introspection annotations
2869 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2882 #############################################################################
2883 # Function : CreateValidSGML
2884 # Description : This turns any chars which are used in SGML into entities,
2885 # e.g. '<' into '<'
2886 # Arguments : $text - the text to turn into proper SGML.
2887 #############################################################################
2889 sub CreateValidSGML {
2891 $text =~ s/&/&/g; # Do this first, or the others get messed up.
2892 $text =~ s/</</g;
2893 $text =~ s/>/>/g;
2894 # browers render single tabs inconsistently
2895 $text =~ s/([^\s])\t([^\s])/$1 $2/g;
2899 #############################################################################
2900 # Function : ConvertSGMLChars
2901 # Description : This is used for text in source code comment blocks, to turn
2902 # chars which are used in SGML into entities, e.g. '<' into
2903 # '<'. Depending on $INLINE_MARKUP_MODE, this is done
2904 # unconditionally or only if the character doesn't seem to be
2905 # part of an SGML construct (tag or entity reference).
2906 # Arguments : $text - the text to turn into proper SGML.
2907 #############################################################################
2909 sub ConvertSGMLChars {
2910 my ($symbol, $text) = @_;
2912 if ($INLINE_MARKUP_MODE) {
2913 # For the XML/SGML mode only convert to entities outside CDATA sections.
2914 return &ModifyXMLElements ($text, $symbol,
2915 "<!\\[CDATA\\[|<programlisting[^>]*>",
2916 \&ConvertSGMLCharsEndTag,
2917 \&ConvertSGMLCharsCallback);
2919 # For the simple non-sgml mode, convert to entities everywhere.
2921 # First, convert freestanding & to &
2922 $text =~ s/&(?![a-zA-Z#]+;)/&/g;
2923 $text =~ s/</</g;
2924 # Allow ">" at beginning of string for blockquote markdown
2925 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2932 sub ConvertSGMLCharsEndTag {
2933 if ($_[0] eq "<!\[CDATA\[") {
2936 return "</programlisting>";
2940 sub ConvertSGMLCharsCallback {
2941 my ($text, $symbol, $tag) = @_;
2943 if ($tag =~ m/^<programlisting/) {
2944 # We can handle <programlisting> specially here.
2945 return &ModifyXMLElements ($text, $symbol,
2947 \&ConvertSGMLCharsEndTag,
2948 \&ConvertSGMLCharsCallback2);
2949 } elsif ($tag eq "") {
2950 # If we're not in CDATA convert to entities.
2951 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2952 $text =~ s/<(?![a-zA-Z\/!])/</g;
2953 # Allow ">" at beginning of string for blockquote markdown
2954 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2956 # Handle "#include <xxxxx>"
2957 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2963 sub ConvertSGMLCharsCallback2 {
2964 my ($text, $symbol, $tag) = @_;
2966 # If we're not in CDATA convert to entities.
2967 # We could handle <programlisting> differently, though I'm not sure it helps.
2969 # replace only if its not a tag
2970 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2971 $text =~ s/<(?![a-zA-Z\/!])/</g;
2972 $text =~ s/(?<![a-zA-Z0-9"'\/-])>/>/g;
2974 # Handle "#include <xxxxx>"
2975 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2981 #############################################################################
2982 # Function : ExpandAnnotation
2983 # Description : This turns annotations into acronym tags.
2984 # Arguments : $symbol - the symbol being documented, for error messages.
2985 # $text - the text to expand.
2986 #############################################################################
2987 sub ExpandAnnotation {
2988 my ($symbol, $param_desc) = @_;
2989 my $param_annotations = "";
2991 # look for annotations at the start of the comment part
2992 # function level annotations don't end with a colon ':'
2993 if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
2998 @annotations = split(/\)\s*\(/,$1);
2999 @TRACE@("annotations for $symbol: '$1'\n");
3000 foreach $annotation (@annotations) {
3001 # need to search for the longest key-match in %AnnotationDefinition
3003 my $match_annotation="";
3005 foreach $annotationdef (keys %AnnotationDefinition) {
3006 if ($annotation =~ m/^$annotationdef/) {
3007 if (length($annotationdef)>$match_length) {
3008 $match_length=length($annotationdef);
3009 $match_annotation=$annotationdef;
3013 my $annotation_extra = "";
3014 if ($match_annotation ne "") {
3015 if ($annotation =~ m%$match_annotation\s+(.*)%) {
3016 $annotation_extra = " $1";
3018 $AnnotationsUsed{$match_annotation} = 1;
3019 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
3022 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3023 "unknown annotation \"$annotation\" in documentation for $symbol.");
3024 $param_annotations .= "[$annotation]";
3028 $param_desc =~ m/^(.*?)\.*\s*$/s;
3029 $param_desc = "$1. ";
3031 if ($param_annotations ne "") {
3032 $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
3034 return ($param_desc, $param_annotations);
3037 #############################################################################
3038 # Function : ExpandAbbreviations
3039 # Description : This turns the abbreviations function(), macro(), @param,
3040 # %constant, and #symbol into appropriate DocBook markup.
3041 # CDATA sections and <programlisting> parts are skipped.
3042 # Arguments : $symbol - the symbol being documented, for error messages.
3043 # $text - the text to expand.
3044 #############################################################################
3046 sub ExpandAbbreviations {
3047 my ($symbol, $text) = @_;
3049 # Note: This is a fallback and normally done in the markdown parser
3051 # Convert "|[" and "]|" into the start and end of program listing examples.
3052 # Support \[<!-- language="C" --> modifiers
3053 $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
3054 $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
3055 $text =~ s%\]\|%]]></programlisting></informalexample>%g;
3057 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
3059 return &ModifyXMLElements ($text, $symbol,
3060 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
3061 \&ExpandAbbreviationsEndTag,
3062 \&ExpandAbbreviationsCallback);
3066 # Returns the end tag (as a regexp) corresponding to the given start tag.
3067 sub ExpandAbbreviationsEndTag {
3068 my ($start_tag) = @_;
3070 if ($start_tag eq "<!\[CDATA\[") {
3072 } elsif ($start_tag eq "<!DOCTYPE") {
3074 } elsif ($start_tag =~ m/<(\w+)/) {
3079 # Called inside or outside each CDATA or <programlisting> section.
3080 sub ExpandAbbreviationsCallback {
3081 my ($text, $symbol, $tag) = @_;
3083 if ($tag =~ m/^<programlisting/) {
3084 # Handle any embedded CDATA sections.
3085 return &ModifyXMLElements ($text, $symbol,
3087 \&ExpandAbbreviationsEndTag,
3088 \&ExpandAbbreviationsCallback2);
3089 } elsif ($tag eq "") {
3090 # NOTE: this is a fallback. It is normally done by the Markdown parser.
3092 # We are outside any CDATA or <programlisting> sections, so we expand
3093 # any gtk-doc abbreviations.
3095 # Convert '@param()'
3096 # FIXME: we could make those also links ($symbol.$2), but that would be less
3097 # useful as the link target is a few lines up or down
3098 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
3100 # Convert 'function()' or 'macro()'.
3101 # if there is abc_*_def() we don't want to make a link to _def()
3102 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
3103 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
3104 # handle #Object.func()
3105 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
3107 # Convert '@param', but not '\@param'.
3108 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
3109 $text =~ s/\\\@/\@/g;
3111 # Convert '%constant', but not '\%constant'.
3112 # Also allow negative numbers, e.g. %-1.
3113 $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
3114 $text =~ s/\\\%/\%/g;
3116 # Convert '#symbol', but not '\#symbol'.
3117 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
3124 # This is called inside a <programlisting>
3125 sub ExpandAbbreviationsCallback2 {
3126 my ($text, $symbol, $tag) = @_;
3129 # We are inside a <programlisting> but outside any CDATA sections,
3130 # so we expand any gtk-doc abbreviations.
3131 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
3132 # why not just call it
3133 $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
3134 } elsif ($tag eq "<![CDATA[") {
3135 # NOTE: this is a fallback. It is normally done by the Markdown parser.
3136 $text = &ReplaceEntities ($text, $symbol);
3143 my ($symbol, $tag) = @_;;
3146 # Check for things like '#include', '#define', and skip them.
3147 if ($PreProcessorDirectives{$symbol}) {
3151 # Get rid of special suffixes ('-struct','-enum').
3152 $text =~ s/-struct$//;
3153 $text =~ s/-enum$//;
3155 # If the symbol is in the form "Object::signal", then change the symbol to
3156 # "Object-signal" and use "signal" as the text.
3157 if ($symbol =~ s/::/-/) {
3161 # If the symbol is in the form "Object:property", then change the symbol to
3162 # "Object--property" and use "property" as the text.
3163 if ($symbol =~ s/:/--/) {
3168 $text = tagify ($text, $tag);
3171 return &MakeXRef($symbol, $text);
3175 #############################################################################
3176 # Function : ModifyXMLElements
3177 # Description : Looks for given XML element tags within the text, and calls
3178 # the callback on pieces of text inside & outside those elements.
3179 # Used for special handling of text inside things like CDATA
3180 # and <programlisting>.
3181 # Arguments : $text - the text.
3182 # $symbol - the symbol currently being documented (only used for
3184 # $start_tag_regexp - the regular expression to match start tags.
3185 # e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3186 # CDATA sections or programlisting elements.
3187 # $end_tag_func - function which is passed the matched start tag
3188 # and should return the appropriate end tag string regexp.
3189 # $callback - callback called with each part of the text. It is
3190 # called with a piece of text, the symbol being
3191 # documented, and the matched start tag or "" if the text
3192 # is outside the XML elements being matched.
3193 #############################################################################
3194 sub ModifyXMLElements {
3195 my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3196 my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3199 while ($text =~ m/$start_tag_regexp/s) {
3200 $before_tag = $`; # Prematch for last successful match string
3201 $start_tag = $&; # Last successful match
3202 $text = $'; # Postmatch for last successful match string
3204 $result .= &$callback ($before_tag, $symbol, "");
3205 $result .= $start_tag;
3207 # get the matching end-tag for current tag
3208 $end_tag_regexp = &$end_tag_func ($start_tag);
3210 if ($text =~ m/$end_tag_regexp/s) {
3215 $result .= &$callback ($before_tag, $symbol, $start_tag);
3216 $result .= $end_tag;
3218 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3219 "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3220 # Just assume it is all inside the tag.
3221 $result .= &$callback ($text, $symbol, $start_tag);
3226 # Handle any remaining text outside the tags.
3227 $result .= &$callback ($text, $symbol, "");
3236 # Adds a tag around some text.
3237 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3239 my ($text, $elem) = @_;
3240 return "<" . $elem . ">" . $text . "</" . $elem . ">";
3243 #############################################################################
3244 # Function : MakeDocHeader
3245 # Description : Builds a docbook header for the given tag
3246 # Arguments : $tag - doctype tag
3247 #############################################################################
3251 my $header = $doctype_header;
3252 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE $tag/;
3254 # fix the path for book since this is one level up
3255 if ($tag eq "book") {
3256 $header =~ s#<!ENTITY % gtkdocentities SYSTEM \"../([a-zA-Z./]+)\">#<!ENTITY % gtkdocentities SYSTEM \"$1\">#;
3263 #############################################################################
3264 # Function : MakeXRef
3265 # Description : This returns a cross-reference link to the given symbol.
3266 # Though it doesn't try to do this for a few standard C types
3267 # that it knows won't be in the documentation.
3268 # Arguments : $symbol - the symbol to try to create a XRef to.
3269 # $text - text text to put inside the XRef, defaults to $symbol
3270 #############################################################################
3273 my ($symbol, $text) = ($_[0], $_[1]);
3275 $symbol =~ s/^\s+//;
3276 $symbol =~ s/\s+$//;
3278 if (!defined($text)) {
3281 # Get rid of special suffixes ('-struct','-enum').
3282 $text =~ s/-struct$//;
3283 $text =~ s/-enum$//;
3286 if ($symbol =~ m/ /) {
3290 @TRACE@("Getting type link for $symbol -> $text\n");
3292 my $symbol_id = &CreateValidSGMLID ($symbol);
3293 return "<link linkend=\"$symbol_id\">$text</link>";
3297 #############################################################################
3298 # Function : MakeIndexterms
3299 # Description : This returns a indexterm elements for the given symbol
3300 # Arguments : $symbol - the symbol to create indexterms for
3301 #############################################################################
3303 sub MakeIndexterms {
3304 my ($symbol, $id) = @_;
3308 # make the index useful, by ommiting the namespace when sorting
3309 if ($NAME_SPACE ne "") {
3310 if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3311 $sortas=" sortas=\"$1\"";
3315 if (exists $Deprecated{$symbol}) {
3316 $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3317 $IndexEntriesDeprecated{$symbol}=$id;
3318 $IndexEntriesFull{$symbol}=$id;
3320 if (exists $Since{$symbol}) {
3321 my $since = $Since{$symbol};
3325 $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3327 $IndexEntriesSince{$symbol}=$id;
3328 $IndexEntriesFull{$symbol}=$id;
3331 $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3332 $IndexEntriesFull{$symbol}=$id;
3338 #############################################################################
3339 # Function : MakeDeprecationNote
3340 # Description : This returns a deprecation warning for the given symbol.
3341 # Arguments : $symbol - the symbol to try to create a warning for.
3342 #############################################################################
3344 sub MakeDeprecationNote {
3345 my ($symbol) = $_[0];
3347 if (exists $Deprecated{$symbol}) {
3350 $desc .= "<warning><para><literal>$symbol</literal> ";
3352 $note = $Deprecated{$symbol};
3354 if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3355 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3357 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3359 $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3363 $note = &ConvertMarkDown($symbol, $note);
3364 $desc .= " " . $note;
3366 $desc .= "</warning>\n";
3371 #############################################################################
3372 # Function : MakeConditionDescription
3373 # Description : This returns a sumary of conditions for the given symbol.
3374 # Arguments : $symbol - the symbol to try to create the sumary.
3375 #############################################################################
3377 sub MakeConditionDescription {
3378 my ($symbol) = $_[0];
3381 if (exists $Deprecated{$symbol}) {
3386 if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3387 $desc .= "deprecated:$1";
3389 $desc .= "deprecated";
3393 if (exists $Since{$symbol}) {
3398 if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3399 $desc .= "since:$1";
3405 if (exists $StabilityLevel{$symbol}) {
3409 $desc .= "stability:".$StabilityLevel{$symbol};
3414 $cond =~ s/\"/"/g;
3415 $desc=" condition=\"".$cond."\"";
3416 @TRACE@("condition for '$symbol' = '$desc'\n");
3421 #############################################################################
3422 # Function : GetHierarchy
3423 # Description : Returns the DocBook output describing the ancestors and
3424 # immediate children of a GObject subclass. It uses the
3425 # global @Objects and @ObjectLevels arrays to walk the tree.
3427 # Arguments : $object - the GtkObject subclass.
3428 # @hierarchy - previous hierarchy
3429 #############################################################################
3432 my ($object,$hierarchy_ref) = @_;
3433 my @hierarchy = @{$hierarchy_ref};
3435 # Find object in the objects array.
3441 for ($i = 0; $i < @Objects; $i++) {
3443 if ($ObjectLevels[$i] <= $level) {
3446 elsif ($ObjectLevels[$i] == $level + 1) {
3447 push (@children, $Objects[$i]);
3450 elsif ($Objects[$i] eq $object) {
3453 $level = $ObjectLevels[$i];
3460 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3462 push (@ancestors, $object);
3463 @TRACE@("Level: $level\n");
3464 while ($level > 1) {
3466 if ($ObjectLevels[$j] < $level) {
3467 push (@ancestors, $Objects[$j]);
3468 $level = $ObjectLevels[$j];
3469 @TRACE@("Level: $level\n");
3473 # Output the ancestors, indented and with links.
3476 for ($i = $#ancestors; $i >= 0; $i--) {
3479 my $ancestor = $ancestors[$i];
3480 my $ancestor_id = &CreateValidSGMLID ($ancestor);
3481 my $indent = ' ' x ($level * 4);
3482 # Don't add a link to the current object, i.e. when i == 0.
3484 $entry_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3485 $alt_text = $indent . $ancestor;
3487 $entry_text = $indent . $ancestor;
3488 $alt_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3490 @TRACE@("Checking for '$entry_text' or '$alt_text'");
3491 # Check if we already have this object
3493 for ($j = 0; $j <= $#hierarchy; $j++) {
3494 if (($hierarchy[$j] eq $entry_text) or ($hierarchy[$j] eq $alt_text)) {
3500 # We have a new entry, find insert position in alphabetical order
3502 for ($j = $last_index; $j <= $#hierarchy; $j++) {
3503 if ($hierarchy[$j] !~ m/^${indent}/) {
3507 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3508 my $stripped_text = $hierarchy[$j];
3509 if ($entry_text !~ m/<link linkend/) {
3510 $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3511 $stripped_text =~ s%</link>%%;
3513 if ($entry_text lt $stripped_text) {
3522 $last_index = 1 + $#hierarchy;
3524 splice @hierarchy, $last_index, 0, ($entry_text);
3527 # Already have this one, make sure we use the not linked version
3528 if ($entry_text !~ m/<link linkend=/) {
3529 $hierarchy[$j] = $entry_text;
3531 # Remember index as base insert point
3532 $last_index = $index + 1;
3536 # Output the children, indented and with links.
3537 for ($i = 0; $i <= $#children; $i++) {
3538 my $id = &CreateValidSGMLID ($children[$i]);
3539 my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3540 splice @hierarchy, $last_index, 0, ($indented_text);
3547 #############################################################################
3548 # Function : GetInterfaces
3549 # Description : Returns the DocBook output describing the interfaces
3550 # implemented by a class. It uses the global %Interfaces hash.
3551 # Arguments : $object - the GtkObject subclass.
3552 #############################################################################
3559 # Find object in the objects array.
3560 if (exists($Interfaces{$object})) {
3561 my @ifaces = split(' ', $Interfaces{$object});
3566 for ($i = 0; $i <= $#ifaces; $i++) {
3567 my $id = &CreateValidSGMLID ($ifaces[$i]);
3568 $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3569 if ($i < $#ifaces - 1) {
3572 elsif ($i < $#ifaces) {
3587 #############################################################################
3588 # Function : GetImplementations
3589 # Description : Returns the DocBook output describing the implementations
3590 # of an interface. It uses the global %Interfaces hash.
3591 # Arguments : $object - the GtkObject subclass.
3592 #############################################################################
3594 sub GetImplementations {
3599 foreach my $key (keys %Interfaces) {
3600 if ($Interfaces{$key} =~ /\b$object\b/) {
3601 push (@impls, $key);
3605 @impls = sort @impls;
3608 $object is implemented by
3610 for ($i = 0; $i <= $#impls; $i++) {
3611 my $id = &CreateValidSGMLID ($impls[$i]);
3612 $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3613 if ($i < $#impls - 1) {
3616 elsif ($i < $#impls) {
3631 #############################################################################
3632 # Function : GetPrerequisites
3633 # Description : Returns the DocBook output describing the prerequisites
3634 # of an interface. It uses the global %Prerequisites hash.
3635 # Arguments : $iface - the interface.
3636 #############################################################################
3638 sub GetPrerequisites {
3643 if (exists($Prerequisites{$iface})) {
3648 my @prereqs = split(' ', $Prerequisites{$iface});
3649 for ($i = 0; $i <= $#prereqs; $i++) {
3650 my $id = &CreateValidSGMLID ($prereqs[$i]);
3651 $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3652 if ($i < $#prereqs - 1) {
3655 elsif ($i < $#prereqs) {
3669 #############################################################################
3670 # Function : GetDerived
3671 # Description : Returns the DocBook output describing the derived interfaces
3672 # of an interface. It uses the global %Prerequisites hash.
3673 # Arguments : $iface - the interface.
3674 #############################################################################
3682 foreach my $key (keys %Prerequisites) {
3683 if ($Prerequisites{$key} =~ /\b$iface\b/) {
3684 push (@derived, $key);
3687 if ($#derived >= 0) {
3688 @derived = sort @derived;
3691 $iface is required by
3693 for ($i = 0; $i <= $#derived; $i++) {
3694 my $id = &CreateValidSGMLID ($derived[$i]);
3695 $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3696 if ($i < $#derived - 1) {
3699 elsif ($i < $#derived) {
3714 #############################################################################
3715 # Function : GetSignals
3716 # Description : Returns the synopsis and detailed description DocBook output
3717 # for the signal handlers of a given GtkObject subclass.
3718 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3719 #############################################################################
3727 for ($i = 0; $i <= $#SignalObjects; $i++) {
3728 if ($SignalObjects[$i] eq $object) {
3729 @TRACE@("Found signal: $SignalNames[$i]\n");
3730 my $name = $SignalNames[$i];
3731 my $symbol = "${object}::${name}";
3732 my $id = &CreateValidSGMLID ("$object-$name");
3734 $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3735 $desc .= MakeIndexterms($symbol, $id);
3737 $desc .= OutputSymbolExtraLinks($symbol);
3739 $desc .= "<programlisting language=\"C\">";
3741 $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3742 my $type_modifier = defined($1) ? $1 : "";
3745 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3747 my $ret_type_output = "$type_modifier$xref$pointer";
3748 my $callback_name = "user_function";
3749 $desc .= "${ret_type_output}\n${callback_name} (";
3751 my $indentation = ' ' x (length($callback_name) + 2);
3752 my $pad = $indentation;
3754 my $sourceparams = $SourceSymbolParams{$symbol};
3755 my @params = split ("\n", $SignalPrototypes[$i]);
3758 my $type_len = length("gpointer");
3759 my $name_len = length("user_data");
3760 # do two passes, the first one is to calculate padding
3761 for ($l = 0; $l < 2; $l++) {
3762 for ($j = 0; $j <= $#params; $j++) {
3764 # allow alphanumerics, '_', '[' & ']' in param names
3765 if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3768 if (defined($sourceparams)) {
3769 $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3774 if (!defined($param_name)) {
3775 $param_name = "arg$j";
3778 if (length($type) + length($pointer) > $type_len) {
3779 $type_len = length($type) + length($pointer);
3781 if (length($param_name) > $name_len) {
3782 $name_len = length($param_name);
3786 $xref = &MakeXRef ($type, &tagify($type, "type"));
3787 $pad = ' ' x ($type_len - length($type) - length($pointer));
3788 $desc .= "$xref$pad $pointer${param_name},\n";
3789 $desc .= $indentation;
3792 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3793 "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3797 $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3798 $pad = ' ' x ($type_len - length("gpointer"));
3799 $desc .= "$xref$pad user_data)";
3800 $desc .= "</programlisting>\n";
3802 my $flags = $SignalFlags[$i];
3803 my $flags_string = "";
3805 if (defined ($flags)) {
3806 if ($flags =~ m/f/) {
3807 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3809 elsif ($flags =~ m/l/) {
3810 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3812 elsif ($flags =~ m/c/) {
3813 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3814 $flags_string = "Cleanup";
3816 if ($flags =~ m/r/) {
3817 if ($flags_string) { $flags_string .= " / "; }
3818 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3820 if ($flags =~ m/d/) {
3821 if ($flags_string) { $flags_string .= " / "; }
3822 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3824 if ($flags =~ m/a/) {
3825 if ($flags_string) { $flags_string .= " / "; }
3826 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3828 if ($flags =~ m/h/) {
3829 if ($flags_string) { $flags_string .= " / "; }
3830 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3834 $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";
3836 my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3838 $AllSymbols{$symbol} = 1;
3839 if (defined ($SymbolDocs{$symbol})) {
3840 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3842 $desc .= $symbol_docs;
3844 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3845 $AllDocumentedSymbols{$symbol} = 1;
3848 if (defined ($SymbolAnnotations{$symbol})) {
3849 my $param_desc = $SymbolAnnotations{$symbol};
3850 my $param_annotations = "";
3851 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3852 if ($param_annotations ne "") {
3853 $desc .= "\n<para>$param_annotations</para>";
3856 $desc .= &MakeDeprecationNote($symbol);
3858 $desc .= $parameters;
3859 if ($flags_string) {
3860 $desc .= "<para>Flags: $flags_string</para>\n";
3862 $desc .= OutputSymbolTraits ($symbol);
3863 $desc .= "</refsect2>";
3866 return ($synop, $desc);
3870 #############################################################################
3871 # Function : GetArgs
3872 # Description : Returns the synopsis and detailed description DocBook output
3873 # for the Args of a given GtkObject subclass.
3874 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3875 #############################################################################
3881 my $child_synop = "";
3882 my $child_desc = "";
3883 my $style_synop = "";
3884 my $style_desc = "";
3887 for ($i = 0; $i <= $#ArgObjects; $i++) {
3888 if ($ArgObjects[$i] eq $object) {
3889 @TRACE@("Found arg: $ArgNames[$i]\n");
3890 my $name = $ArgNames[$i];
3891 my $flags = $ArgFlags[$i];
3892 my $flags_string = "";
3896 if ($flags =~ m/c/) {
3897 $kind = "child property";
3900 elsif ($flags =~ m/s/) {
3901 $kind = "style property";
3908 # Remember only one colon so we don't clash with signals.
3909 my $symbol = "${object}:${name}";
3910 # use two dashes and ev. an extra separator here for the same reason.
3911 my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3913 my $type = $ArgTypes[$i];
3915 my $range = $ArgRanges[$i];
3916 my $range_output = CreateValidSGML ($range);
3917 my $default = $ArgDefaults[$i];
3918 my $default_output = CreateValidSGML ($default);
3920 if ($type eq "GtkString") {
3921 $type = "char *";
3923 if ($type eq "GtkSignal") {
3924 $type = "GtkSignalFunc, gpointer";
3925 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3926 . &MakeXRef ("gpointer");
3927 } elsif ($type =~ m/^(\w+)\*$/) {
3928 $type_output = &MakeXRef ($1, &tagify($1, "type")) . " *";
3930 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3933 if ($flags =~ m/r/) {
3934 $flags_string = "Read";
3936 if ($flags =~ m/w/) {
3937 if ($flags_string) { $flags_string .= " / "; }
3938 $flags_string .= "Write";
3940 if ($flags =~ m/x/) {
3941 if ($flags_string) { $flags_string .= " / "; }
3942 $flags_string .= "Construct";
3944 if ($flags =~ m/X/) {
3945 if ($flags_string) { $flags_string .= " / "; }
3946 $flags_string .= "Construct Only";
3949 $AllSymbols{$symbol} = 1;
3951 if (defined($SymbolDocs{$symbol}) &&
3952 !IsEmptyDoc($SymbolDocs{$symbol})) {
3953 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3954 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3955 $AllDocumentedSymbols{$symbol} = 1;
3958 if ($ArgBlurbs[$i] ne "") {
3959 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3960 $AllDocumentedSymbols{$symbol} = 1;
3962 # FIXME: print a warning?
3963 @TRACE@(".. no description\n");
3968 if (length ($name) < 24) {
3969 $pad1 = " " x (24 - length ($name));
3972 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";
3973 my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3974 $arg_desc .= MakeIndexterms($symbol, $id);
3976 $arg_desc .= OutputSymbolExtraLinks($symbol);
3978 $arg_desc .= "<programlisting> “$name”$pad1 $type_output</programlisting>\n";
3979 $arg_desc .= $blurb;
3980 if (defined ($SymbolAnnotations{$symbol})) {
3981 my $param_desc = $SymbolAnnotations{$symbol};
3982 my $param_annotations = "";
3983 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3984 if ($param_annotations ne "") {
3985 $arg_desc .= "\n<para>$param_annotations</para>";
3988 $arg_desc .= &MakeDeprecationNote($symbol);
3990 if ($flags_string) {
3991 $arg_desc .= "<para>Flags: $flags_string</para>\n";
3994 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3996 if ($default ne "") {
3997 $arg_desc .= "<para>Default value: $default_output</para>\n";
3999 $arg_desc .= OutputSymbolTraits ($symbol);
4000 $arg_desc .= "</refsect2>\n";
4002 if ($flags =~ m/c/) {
4003 $child_synop .= $arg_synop;
4004 $child_desc .= $arg_desc;
4006 elsif ($flags =~ m/s/) {
4007 $style_synop .= $arg_synop;
4008 $style_desc .= $arg_desc;
4011 $synop .= $arg_synop;
4016 return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
4020 #############################################################################
4021 # Function : ReadSourceDocumentation
4022 # Description : This reads in the documentation embedded in comment blocks
4023 # in the source code (for Gnome).
4025 # Parameter descriptions override any in the template files.
4026 # Function descriptions are placed before any description from
4027 # the template files.
4029 # It recursively descends the source directory looking for .c
4030 # files and scans them looking for specially-formatted comment
4033 # Arguments : $source_dir - the directory to scan.
4034 #############m###############################################################
4036 sub ReadSourceDocumentation {
4037 my ($source_dir) = @_;
4038 my ($file, $dir, @suffix_list, $suffix);
4040 # prepend entries from @SOURCE_DIR
4041 for my $dir (@SOURCE_DIRS) {
4042 # Check if the filename is in the ignore list.
4043 if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
4044 @TRACE@("Skipping source directory: $source_dir");
4047 @TRACE@("No match for: ".($1 || $source_dir));
4051 @TRACE@("Scanning source directory: $source_dir");
4053 # This array holds any subdirectories found.
4056 @suffix_list = split (/,/, $SOURCE_SUFFIXES);
4058 opendir (SRCDIR, $source_dir)
4059 || die "Can't open source directory $source_dir: $!";
4061 foreach $file (readdir (SRCDIR)) {
4062 if ($file =~ /^\./) {
4064 } elsif (-d "$source_dir/$file") {
4065 push (@subdirs, $file);
4066 } elsif (@suffix_list) {
4067 foreach $suffix (@suffix_list) {
4068 if ($file =~ m/\.\Q${suffix}\E$/) {
4069 &ScanSourceFile ("$source_dir/$file");
4072 } elsif ($file =~ m/\.[ch]$/) {
4073 &ScanSourceFile ("$source_dir/$file");
4078 # Now recursively scan the subdirectories.
4079 foreach $dir (@subdirs) {
4080 &ReadSourceDocumentation ("$source_dir/$dir");
4085 #############################################################################
4086 # Function : ScanSourceFile
4087 # Description : Scans one source file looking for specially-formatted comment
4088 # blocks. Later &MergeSourceDocumentation is used to merge any
4089 # documentation found with the documentation already read in
4090 # from the template files.
4092 # Arguments : $file - the file to scan.
4093 #############################################################################
4095 sub ScanSourceFile {
4099 # prepend entries from @SOURCE_DIR
4100 for my $dir (@SOURCE_DIRS) {
4101 # Check if the filename is in the ignore list.
4102 if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
4103 @TRACE@("Skipping source file: $file");
4108 if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
4111 &LogWarning ($file, 1, "Can't find basename for this filename.");
4115 # Check if the basename is in the list of files to ignore.
4116 if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
4117 @TRACE@("Skipping source file: $file");
4121 @TRACE@("Scanning source file: $file");
4123 open (SRCFILE, $file)
4124 || die "Can't open $file: $!";
4125 my $in_comment_block = 0;
4128 my ($description, $return_desc);
4129 my ($since_desc, $stability_desc, $deprecated_desc);
4133 # Look for the start of a comment block.
4134 if (!$in_comment_block) {
4135 if (m%^\s*/\*.*\*/%) {
4136 #one-line comment - not gtkdoc
4137 } elsif (m%^\s*/\*\*\s%) {
4138 @TRACE@("Found comment block start\n");
4140 $in_comment_block = 1;
4142 # Reset all the symbol data.
4148 $deprecated_desc = "";
4149 $stability_desc = "";
4150 $current_param = -1;
4156 # We're in a comment block. Check if we've found the end of it.
4159 # maybe its not even meant to be a gtk-doc comment?
4160 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
4162 # Add the return value description onto the end of the params.
4164 # TODO(ensonic): check for duplicated Return docs
4165 # &LogWarning ($file, $., "Multiple Returns for $symbol.");
4166 push (@params, "Returns");
4167 push (@params, $return_desc);
4169 # Convert special characters
4170 $description = &ConvertSGMLChars ($symbol, $description);
4172 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4173 $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
4176 # Handle Section docs
4177 if ($symbol =~ m/SECTION:\s*(.*)/) {
4181 if (scalar %KnownSymbols) {
4182 if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
4183 &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
4187 @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
4188 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4189 @TRACE@(" '".$params[$k]."'\n");
4190 $params[$k] = "\L$params[$k]";
4192 if ($params[$k] eq "short_description") {
4193 $key = "$TMPL_DIR/$real_symbol:Short_Description";
4194 } elsif ($params[$k] eq "see_also") {
4195 $key = "$TMPL_DIR/$real_symbol:See_Also";
4196 } elsif ($params[$k] eq "title") {
4197 $key = "$TMPL_DIR/$real_symbol:Title";
4198 } elsif ($params[$k] eq "stability") {
4199 $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4200 } elsif ($params[$k] eq "section_id") {
4201 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4202 } elsif ($params[$k] eq "include") {
4203 $key = "$TMPL_DIR/$real_symbol:Include";
4204 } elsif ($params[$k] eq "image") {
4205 $key = "$TMPL_DIR/$real_symbol:Image";
4207 if (defined($key)) {
4208 $SourceSymbolDocs{$key}=$params[$k+1];
4209 $SourceSymbolSourceFile{$key} = $file;
4210 $SourceSymbolSourceLine{$key} = $.;
4213 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4214 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4215 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4216 #$SourceSymbolTypes{$symbol} = "SECTION";
4217 } elsif ($symbol =~ m/PROGRAM:\s*(.*)/) {
4218 my $real_symbol = $1;
4222 @TRACE@("PROGRAM DOCS found in source for '$real_symbol'\n");
4223 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4224 @TRACE@(" '".$params[$k]."'\n");
4225 $params[$k] = "\L$params[$k]";
4228 if ($params[$k] eq "short_description") {
4229 $key = "$TMPL_DIR/$real_symbol:Short_Description";
4230 } elsif ($params[$k] eq "see_also") {
4231 $key = "$TMPL_DIR/$real_symbol:See_Also";
4232 } elsif ($params[$k] eq "section_id") {
4233 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4234 } elsif ($params[$k] eq "synopsis") {
4235 $key = "$TMPL_DIR/$real_symbol:Synopsis";
4236 } elsif ($params[$k] eq "returns") {
4237 $key = "$TMPL_DIR/$real_symbol:Returns";
4238 } elsif ($params[$k] =~ m/^(-.*)/) {
4239 $key = "$TMPL_DIR/$real_symbol:Options";
4241 if (defined($SourceSymbolDocs{$key})) {
4242 $opts = $SourceSymbolDocs{$key};
4246 push (@{ $opts }, $1);
4247 push (@{ $opts }, $params[$k+1]);
4249 $SourceSymbolDocs{$key} = $opts;
4252 if (defined($key)) {
4253 $SourceSymbolDocs{$key}=$params[$k+1];
4254 $SourceSymbolSourceFile{$key} = $file;
4255 $SourceSymbolSourceLine{$key} = $.;
4258 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4259 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4260 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4262 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Section_Id"};
4263 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
4264 # Remove trailing blanks and use as is
4265 $section_id =~ s/\s+$//;
4267 $section_id = &CreateValidSGMLID ("$MODULE-$real_symbol");
4270 &OutputProgramDBFile ($real_symbol, $section_id);
4273 @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4274 $SourceSymbolDocs{$symbol} = $description;
4275 $SourceSymbolParams{$symbol} = [ @params ];
4276 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4277 #if (defined $DeclarationTypes{$symbol}) {
4278 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4280 $SourceSymbolSourceFile{$symbol} = $file;
4281 $SourceSymbolSourceLine{$symbol} = $.;
4285 ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4286 $since_desc =~ s/^\s+//;
4287 $since_desc =~ s/\s+$//;
4288 @TRACE@("Since($symbol) : [$since_desc]\n");
4289 $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4290 if(scalar @extra_lines) {
4291 &LogWarning ($file, $., "multi-line since docs found");
4295 if ($stability_desc) {
4296 $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4297 $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4300 if ($deprecated_desc) {
4301 if (!exists $Deprecated{$symbol}) {
4302 # don't warn for signals and properties
4303 #if ($symbol !~ m/::?(.*)/) {
4304 if (defined $DeclarationTypes{$symbol}) {
4305 &LogWarning ($file, $.,
4306 "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4307 " (See the --deprecated-guards option for gtkdoc-scan.)");
4310 $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4314 $in_comment_block = 0;
4318 # Get rid of ' * ' at start of every line in the comment block.
4320 # But make sure we don't get rid of the newline at the end.
4324 @TRACE@("scanning :$_");
4326 # If we haven't found the symbol name yet, look for it.
4328 if (m%^\s*(SECTION:\s*\S+)%) {
4330 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4331 } elsif (m%^\s*(PROGRAM:\s*\S+)%) {
4333 @TRACE@("PROGRAM DOCS found in source for : '$symbol'\n");
4334 } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$%) {
4336 my $annotation = $2;
4337 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4338 if (defined($annotation)) {
4340 if ($annotation ne "") {
4341 $SymbolAnnotations{$symbol} = $annotation;
4342 @TRACE@("remaining text for $symbol: '$annotation'\n");
4349 if ($in_part eq "description") {
4350 # Get rid of 'Description:'
4351 s%^\s*Description:%%;
4354 if (m%^\s*(returns|return\s+value):%i) {
4355 # we're in param section and have not seen the blank line
4356 if($in_part ne "") {
4358 $in_part = "return";
4361 } elsif (m%^\s*since:%i) {
4362 # we're in param section and have not seen the blank line
4363 if($in_part ne "param") {
4368 } elsif (m%^\s*deprecated:%i) {
4369 # we're in param section and have not seen the blank line
4370 if($in_part ne "param") {
4371 $deprecated_desc = $';
4372 $in_part = "deprecated";
4375 } elsif (m%^\s*stability:%i) {
4376 $stability_desc = $';
4377 $in_part = "stability";
4381 if ($in_part eq "description") {
4384 } elsif ($in_part eq "return") {
4387 } elsif ($in_part eq "since") {
4390 } elsif ($in_part eq "stability") {
4391 $stability_desc .= $_;
4393 } elsif ($in_part eq "deprecated") {
4394 $deprecated_desc .= $_;
4398 # We must be in the parameters. Check for the empty line below them.
4400 $in_part = "description";
4404 # Look for a parameter name.
4405 if (m%^\s*@(.+?)\s*:\s*%) {
4406 my $param_name = $1;
4407 my $param_desc = $';
4409 @TRACE@("Found parameter: $param_name\n");
4410 # Allow varargs variations
4411 if ($param_name =~ m/^\.\.\.$/) {
4412 $param_name = "...";
4414 @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4416 push (@params, $param_name);
4417 push (@params, $param_desc);
4418 $current_param += $PARAM_FIELD_COUNT;
4421 } elsif ($in_part eq "") {
4422 @TRACE@("continuation for $symbol annotation '$_'");
4423 my $annotation = $_;
4424 $annotation =~ s/^\s+|\s+$//g ;
4425 $SymbolAnnotations{$symbol} .= $annotation;
4429 # We must be in the middle of a parameter description, so add it on
4430 # to the last element in @params.
4431 if ($current_param == -1) {
4432 &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
4434 $params[$#params] .= $_;
4440 #############################################################################
4441 # Function : OutputMissingDocumentation
4442 # Description : Outputs report of documentation coverage to a file
4445 #############################################################################
4447 sub OutputMissingDocumentation {
4448 my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4449 my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4451 my $n_documented = 0;
4452 my $n_incomplete = 0;
4458 my $buffer_deprecated = "";
4459 my $buffer_descriptions = "";
4461 open(UNDOCUMENTED, ">$new_undocumented_file")
4462 || die "Can't create $new_undocumented_file";
4464 foreach $symbol (sort (keys (%AllSymbols))) {
4465 # FIXME: should we print LogWarnings for undocumented stuff?
4467 #my $ssfile = &GetSymbolSourceFile($symbol);
4468 #my $ssline = &GetSymbolSourceLine($symbol);
4469 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4471 if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4473 if (exists ($AllDocumentedSymbols{$symbol})) {
4475 if (exists ($AllIncompleteSymbols{$symbol})) {
4477 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4478 #$buffer .= "\t0: ".$location;
4480 } elsif (exists $Deprecated{$symbol}) {
4481 if (exists ($AllIncompleteSymbols{$symbol})) {
4483 $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4484 #$buffer .= "\t1a: ".$location;
4486 $buffer_deprecated .= $symbol . "\n";
4487 #$buffer .= "\t1b: ".$location;
4490 if (exists ($AllIncompleteSymbols{$symbol})) {
4492 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4493 #$buffer .= "\t2a: ".$location;
4495 $buffer .= $symbol . "\n";
4496 #$buffer .= "\t2b: ".$location;
4499 } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4501 if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4502 || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4505 # cut off the leading namespace ($TMPL_DIR)
4506 $symbol =~ m/^.*\/(.*)$/;
4507 $buffer_descriptions .= $1 . "\n";
4515 $percent = ($n_documented / $total) * 100.0;
4518 printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4519 print UNDOCUMENTED "$n_documented symbols documented.\n";
4520 print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4521 print UNDOCUMENTED ($total - $n_documented) . " not documented.\n";
4523 if ($buffer_deprecated ne "") {
4524 $buffer .= "\n" . $buffer_deprecated;
4526 if ($buffer_descriptions ne "") {
4527 $buffer .= "\n" . $buffer_descriptions;
4529 if ($buffer ne "") {
4530 print UNDOCUMENTED "\n\n$buffer";
4532 close (UNDOCUMENTED);
4534 return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4536 printf "%.0f%% symbol docs coverage", $percent;
4537 print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4538 print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4542 #############################################################################
4543 # Function : OutputUndeclaredSymbols
4544 # Description : Outputs symbols that are listed in the section file, but not
4545 # declaration is found in the sources
4548 #############################################################################
4550 sub OutputUndeclaredSymbols {
4551 my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4552 my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4554 open(UNDECLARED, ">$new_undeclared_file")
4555 || die "Can't create $new_undeclared_file";
4557 if (%UndeclaredSymbols) {
4558 print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4559 print UNDECLARED "\n";
4560 print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4564 return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4567 #############################################################################
4568 # Function : OutputUnusedSymbols
4569 # Description : Outputs symbols that are documented in comments, but not
4570 # declared in the sources
4573 #############################################################################
4575 sub OutputUnusedSymbols {
4577 my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4578 my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4580 open (UNUSED, ">$new_unused_file")
4581 || die "Can't open $new_unused_file";
4583 foreach $symbol (sort keys (%Declarations)) {
4584 if (!defined ($DeclarationOutput{$symbol})) {
4585 print (UNUSED "$symbol\n");
4589 foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4590 print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4594 if ($num_unused != 0) {
4595 &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4596 "They should be added to $MODULE-sections.txt in the appropriate place.");
4599 return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4603 #############################################################################
4604 # Function : OutputAllSymbols
4605 # Description : Outputs list of all symbols to a file
4608 #############################################################################
4610 sub OutputAllSymbols {
4611 my $n_documented = 0;
4617 open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4618 || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4620 foreach $symbol (sort (keys (%AllSymbols))) {
4621 print SYMBOLS $symbol . "\n";
4627 #############################################################################
4628 # Function : OutputSymbolsWithoutSince
4629 # Description : Outputs list of all symbols without a since tag to a file
4632 #############################################################################
4634 sub OutputSymbolsWithoutSince {
4635 my $n_documented = 0;
4641 open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4642 || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4644 foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4645 if (!defined $Since{$symbol}) {
4646 print SYMBOLS $symbol . "\n";
4654 #############################################################################
4655 # Function : MergeSourceDocumentation
4656 # Description : This merges documentation read from a source file into the
4657 # documentation read in from a template file.
4659 # Parameter descriptions override any in the template files.
4660 # Function descriptions are placed before any description from
4661 # the template files.
4664 #############################################################################
4666 sub MergeSourceDocumentation {
4670 if (scalar %SymbolDocs) {
4671 @Symbols=keys (%SymbolDocs);
4672 @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4675 # filter scanned declarations, with what we suppress from -sections.txt
4677 foreach $symbol (keys (%Declarations)) {
4678 if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4682 # , add the rest from -sections.txt
4683 foreach $symbol (keys (%KnownSymbols)) {
4684 if ($KnownSymbols{$symbol} == 1) {
4688 # and add whats found in the source
4689 foreach $symbol (keys (%SourceSymbolDocs)) {
4692 @Symbols = keys (%tmp);
4693 @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4695 foreach $symbol (@Symbols) {
4696 $AllSymbols{$symbol} = 1;
4698 my $have_tmpl_docs = 0;
4700 ## see if the symbol is documented in template
4701 my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4702 my $check_tmpl_doc =$tmpl_doc;
4703 # remove all xml-tags and whitespaces
4704 $check_tmpl_doc =~ s/<.*?>//g;
4705 $check_tmpl_doc =~ s/\s//g;
4707 if ($check_tmpl_doc ne "") {
4708 $have_tmpl_docs = 1;
4710 # if the docs have just an empty para, don't merge that.
4711 $check_tmpl_doc = $tmpl_doc;
4712 $check_tmpl_doc =~ s/(\s|\n)//msg;
4713 if ($check_tmpl_doc eq "<para></para>") {
4718 if (exists ($SourceSymbolDocs{$symbol})) {
4719 my $type = $DeclarationTypes {$symbol};
4721 @TRACE@("merging [$symbol] from source\n");
4723 my $item = "Parameter";
4724 if (defined ($type)) {
4725 if ($type eq 'STRUCT') {
4727 } elsif ($type eq 'ENUM') {
4729 } elsif ($type eq 'UNION') {
4736 my $src_doc = $SourceSymbolDocs{$symbol};
4737 # remove leading and training whitespaces
4738 $src_doc =~ s/^\s+//;
4739 $src_doc =~ s/\s+$//;
4741 # Don't output warnings for overridden titles as titles are
4742 # automatically generated in the -sections.txt file, and thus they
4743 # are often overridden.
4744 if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4745 # check if content is different
4746 if ($tmpl_doc ne $src_doc) {
4747 #print "[$tmpl_doc] [$src_doc]\n";
4748 &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4749 "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4753 if ($src_doc ne "") {
4754 $AllDocumentedSymbols{$symbol} = 1;
4757 # Do not add <para> to nothing, it breaks missing docs checks.
4758 my $src_doc_para = "";
4759 if ($src_doc ne "") {
4760 $src_doc_para = $src_doc;
4763 if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4764 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4765 } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4766 # For the title/summary/see also section docs we don't want to
4767 # add any <para> tags.
4768 $SymbolDocs{$symbol} = "$src_doc"
4770 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4774 if ($symbol =~ m/.*::.*/) {
4775 # For signals we prefer the param names from the source docs,
4776 # since the ones from the templates are likely to contain the
4777 # artificial argn names which are generated by gtkdoc-scangobj.
4778 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4779 # FIXME: we need to check for empty docs here as well!
4781 # The templates contain the definitive parameter names and order,
4782 # so we will not change that. We only override the actual text.
4783 my $tmpl_params = $SymbolParams{$symbol};
4784 if (!defined ($tmpl_params)) {
4785 @TRACE@("No merge needed for $symbol\n");
4786 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4787 # FIXME: we still like to get the number of params and merge
4788 # 1) we would noticed that params have been removed/renamed
4789 # 2) we would catch undocumented params
4790 # params are not (yet) exported in -decl.txt so that we
4791 # could easily grab them :/
4793 my $params = $SourceSymbolParams{$symbol};
4795 @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4796 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4797 my $tmpl_param_name = $$tmpl_params[$j];
4799 # Try to find the param in the source comment documentation.
4802 @TRACE@(" try merge param $tmpl_param_name\n");
4803 for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4804 my $param_name = $$params[$k];
4805 my $param_desc = $$params[$k + 1];
4807 @TRACE@(" test param $param_name\n");
4808 # We accept changes in case, since the Gnome source
4809 # docs contain a lot of these.
4810 if ("\L$param_name" eq "\L$tmpl_param_name") {
4813 # Override the description.
4814 $$tmpl_params[$j + 1] = $param_desc;
4816 # Set the name to "" to mark it as used.
4822 # If it looks like the parameters are there, but not
4823 # in the right place, try to explain a bit better.
4824 if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4825 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4826 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4830 # Now we output a warning if parameters have been described which
4832 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4833 my $param_name = $$params[$j];
4835 # the template builder cannot detect if a macro returns
4837 if(($type eq "MACRO") && ($param_name eq "Returns")) {
4838 # FIXME: do we need to add it then to tmpl_params[] ?
4839 my $num=$#$tmpl_params;
4840 @TRACE@(" adding Returns: to macro docs for $symbol.\n");
4841 $$tmpl_params[$num+1]="Returns";
4842 $$tmpl_params[$num+2]=$$params[$j+1];
4845 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4846 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4852 if ($have_tmpl_docs) {
4853 $AllDocumentedSymbols{$symbol} = 1;
4854 @TRACE@("merging [$symbol] from template\n");
4857 @TRACE@("[$symbol] undocumented\n");
4861 # if this symbol is documented, check if docs are complete
4862 $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4863 # remove all xml-tags and whitespaces
4864 $check_tmpl_doc =~ s/<.*?>//g;
4865 $check_tmpl_doc =~ s/\s//g;
4866 if ($check_tmpl_doc ne "") {
4867 my $tmpl_params = $SymbolParams{$symbol};
4868 if (defined ($tmpl_params)) {
4869 my $type = $DeclarationTypes {$symbol};
4871 my $item = "Parameter";
4872 if (defined ($type)) {
4873 if ($type eq 'STRUCT') {
4875 } elsif ($type eq 'ENUM') {
4877 } elsif ($type eq 'UNION') {
4884 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4886 if ($#$tmpl_params > 0) {
4888 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4889 # Output a warning if the parameter is empty and
4890 # remember for stats.
4891 my $tmpl_param_name = $$tmpl_params[$j];
4892 my $tmpl_param_desc = $$tmpl_params[$j + 1];
4893 if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4894 if (exists ($AllIncompleteSymbols{$symbol})) {
4895 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4897 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4899 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4900 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4905 if ($#$tmpl_params == 0) {
4906 $AllIncompleteSymbols{$symbol}="<items>";
4907 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4908 "$item descriptions for $symbol are missing in source code comment block.");
4910 # $#$tmpl_params==-1 means we don't know about parameters
4911 # this unfortunately does not tell if there should be some
4916 @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4919 #############################################################################
4920 # Function : IsEmptyDoc
4921 # Description : Check if a doc-string is empty. Its also regarded as empty if
4922 # it only consist of whitespace or e.g. FIXME.
4923 # Arguments : the doc-string
4924 #############################################################################
4928 if ($doc =~ /^\s*$/) {
4932 if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4939 #############################################################################
4940 # Function : ConvertMarkDown
4941 # Description : Converts mark down syntax to the respective docbook.
4942 # http://de.wikipedia.org/wiki/Markdown
4943 # Inspired by the design of ParseDown
4944 # http://parsedown.org/
4945 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4946 # Arguments : the symbol name, the doc-string
4947 #############################################################################
4949 sub ConvertMarkDown {
4950 my ($symbol, $text) = @_;
4952 $text = &MarkDownParse ($text, $symbol);
4957 # SUPPORTED MARKDOWN
4958 # ==================
4967 # Setext-style Headers
4968 # --------------------
4976 # Ordered (unnested) Lists
4977 # ------------------------
4981 # 1. item 2 with loooong
4986 # Note: we require a blank line above the list items
4989 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4991 sub MarkDownParseBlocks {
4992 my ($linesref, $symbol, $context) = @_;
4995 my $md_block = { type => "" };
4997 OUTER: foreach $line (@$linesref) {
4998 my $first_char = substr ($line, 0, 1);
4999 my $deindented_line;
5001 @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
5003 if ($md_block->{"type"} eq "markup") {
5004 if (!$md_block->{"closed"}) {
5005 if (index ($line, $md_block->{"start"}) != -1) {
5006 $md_block->{"depth"}++;
5008 if (index ($line, $md_block->{"end"}) != -1) {
5009 if ($md_block->{"depth"} > 0) {
5010 $md_block->{"depth"}--;
5012 @TRACE@("closing tag '$line'");
5013 $md_block->{"closed"} = 1;
5014 # TODO(ensonic): reparse inner text with MarkDownParseLines?
5017 $md_block->{"text"} .= "\n" . $line;
5018 @TRACE@("add to markup");
5023 $deindented_line = $line;
5024 $deindented_line =~ s/^\s+//;
5026 if ($md_block->{"type"} eq "heading") {
5027 # a heading is ended by any level less than or equal
5028 if ($md_block->{"level"} == 1) {
5029 if ($line =~ /^={4,}[ \t]*$/) {
5030 my $text = pop @{$md_block->{"lines"}};
5031 $md_block->{"interrupted"} = 0;
5032 push @md_blocks, $md_block;
5034 $md_block = { type => "heading",
5039 } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5040 $md_block->{"interrupted"} = 0;
5041 push @md_blocks, $md_block;
5043 $md_block = { type => "heading",
5050 # push lines into the block until the end is reached
5051 push @{$md_block->{"lines"}}, $line;
5055 if ($line =~ /^[=]{4,}[ \t]*$/) {
5056 my $text = pop @{$md_block->{"lines"}};
5057 $md_block->{"interrupted"} = 0;
5058 push @md_blocks, $md_block;
5060 $md_block = { type => "heading",
5065 } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
5066 my $text = pop @{$md_block->{"lines"}};
5067 $md_block->{"interrupted"} = 0;
5068 push @md_blocks, $md_block;
5070 $md_block = { type => "heading",
5075 } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5076 $md_block->{"interrupted"} = 0;
5077 push @md_blocks, $md_block;
5079 $md_block = { type => "heading",
5083 level => length($1) };
5086 # push lines into the block until the end is reached
5087 push @{$md_block->{"lines"}}, $line;
5091 } elsif ($md_block->{"type"} eq "code") {
5092 if ($line =~ /^[ \t]*\]\|(.*)/) {
5093 push @md_blocks, $md_block;
5094 $md_block = { type => "paragraph",
5098 push @{$md_block->{"lines"}}, $line;
5103 if ($deindented_line eq "") {
5104 $md_block->{"interrupted"} = 1;
5108 if ($md_block->{"type"} eq "quote") {
5109 if (!$md_block->{"interrupted"}) {
5110 $line =~ s/^[ ]*>[ ]?//;
5111 push @{$md_block->{"lines"}}, $line;
5114 } elsif ($md_block->{"type"} eq "li") {
5115 my $marker = $md_block->{"marker"};
5116 if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
5117 my $indentation = $1;
5118 if ($md_block->{"indentation"} ne $indentation) {
5119 push @{$md_block->{"lines"}}, $line;
5122 my $ordered = $md_block->{"ordered"};
5123 $lines =~ s/^[ ]{0,4}//;
5124 $md_block->{"last"} = 0;
5125 push @md_blocks, $md_block;
5126 $md_block = { type => "li",
5127 ordered => $ordered,
5128 indentation => $indentation,
5132 lines => [ $lines ] };
5137 if ($md_block->{"interrupted"}) {
5138 if ($first_char eq " ") {
5139 push @{$md_block->{"lines"}}, "";
5140 $line =~ s/^[ ]{0,4}//;
5141 push @{$md_block->{"lines"}}, $line;
5142 $md_block->{"interrupted"} = 0;
5146 $line =~ s/^[ ]{0,4}//;
5147 push @{$md_block->{"lines"}}, $line;
5152 # indentation sensitive types
5153 @TRACE@("parsing '$line'");
5155 if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5157 push @md_blocks, $md_block;
5159 $md_block = { type => "heading",
5163 level => length($1) };
5166 } elsif ($line =~ /^={4,}[ \t]*$/) {
5167 # setext heading (====)
5169 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
5170 push @md_blocks, $md_block;
5171 $md_block->{"type"} = "heading";
5172 $md_block->{"lines"} = [];
5173 $md_block->{"level"} = 1;
5177 } elsif ($line =~ /^-{4,}[ \t]*$/) {
5178 # setext heading (-----)
5180 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
5181 push @md_blocks, $md_block;
5182 $md_block->{"type"} = "heading";
5183 $md_block->{"lines"} = [];
5184 $md_block->{"level"} = 2;
5188 } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
5190 $md_block->{"interrupted"} = 1;
5191 push @md_blocks, $md_block;
5192 $md_block = { type => "code",
5198 # indentation insensitive types
5199 if ($line =~ /^[ ]*<!DOCTYPE/) {
5200 push @md_blocks, $md_block;
5202 $md_block = { type => "markup",
5203 text => $deindented_line,
5209 } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
5210 # markup, including <?xml version="1.0"?>
5212 my $is_self_closing = defined($2);
5214 # skip link markdown
5215 # TODO(ensonic): consider adding more uri schemes (ftp, ...)
5216 if ($tag =~ /^https?/) {
5217 @TRACE@("skipping link '$tag'");
5219 # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
5220 # instead of creation a markdown block.
5221 my $scanning_for_end_of_text_level_tag = (
5222 $md_block->{"type"} eq "paragraph" &&
5223 defined($md_block->{"start"}) &&
5224 !$md_block->{"closed"});
5225 @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
5226 if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
5227 push @md_blocks, $md_block;
5229 if ($is_self_closing) {
5230 @TRACE@("self-closing docbook '$tag'");
5231 $md_block = { type => "self-closing tag",
5232 text => $deindented_line };
5233 $is_self_closing = 0;
5237 @TRACE@("new markup '$tag'");
5238 $md_block = { type => "markup",
5239 text => $deindented_line,
5240 start => "<" . $tag . ">",
5241 end => "</" . $tag . ">",
5244 if ($deindented_line =~ /<\/$tag>/) {
5245 $md_block->{"closed"} = 1;
5249 if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
5250 @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
5251 # TODO(ensonic): handle nesting
5252 if (!$scanning_for_end_of_text_level_tag) {
5253 if ($deindented_line !~ /<\/$tag>/) {
5254 @TRACE@("new text level markup '$tag'");
5255 $md_block->{"start"} = "<" . $tag . ">";
5256 $md_block->{"end"} = "</" . $tag . ">";
5257 $md_block->{"closed"} = 0;
5258 @TRACE@("scanning for end of '$tag'");
5261 if ($deindented_line =~ /$md_block->{"end"}/) {
5262 $md_block->{"closed"} = 1;
5263 @TRACE@("found end of '$tag'");
5269 } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
5271 push @md_blocks, $md_block;
5273 my $indentation = $1;
5274 $lines =~ s/^[ ]{0,4}//;
5275 $md_block = { type => "li",
5277 indentation => $indentation,
5281 lines => [ $lines ] };
5283 } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
5284 push @md_blocks, $md_block;
5285 $md_block = { type => "quote",
5291 if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5292 push @md_blocks, $md_block;
5294 my $indentation = $1;
5295 $lines =~ s/^[ ]{0,4}//;
5297 $md_block = { type => "li",
5299 indentation => $indentation,
5300 marker => "\\d+[.]",
5303 lines => [ $lines ] };
5309 if ($md_block->{"type"} eq "paragraph") {
5310 if ($md_block->{"interrupted"}) {
5311 push @md_blocks, $md_block;
5312 $md_block = { type => "paragraph",
5315 @TRACE@("new paragraph due to interrupted");
5317 $md_block->{"text"} .= "\n" . $line;
5318 @TRACE@("add to paragraph");
5321 push @md_blocks, $md_block;
5322 $md_block = { type => "paragraph",
5324 @TRACE@("new paragraph due to different block type");
5328 push @md_blocks, $md_block;
5335 sub MarkDownParseSpanElementsInner {
5336 my ($text, $markersref) = @_;
5338 my %markers = map { $_ => 1 } @$markersref;
5340 while ($text ne "") {
5341 my $closest_marker = "";
5342 my $closest_marker_index = 0;
5343 my $closest_marker_position = -1;
5344 my $text_marker = "";
5351 while ( ($marker, $use) = each %markers ) {
5352 my $marker_position;
5358 $marker_position = index ($text, $marker);
5360 if ($marker_position < 0) {
5361 $markers{$marker} = 0;
5365 if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5366 $closest_marker = $marker;
5367 $closest_marker_index = $i;
5368 $closest_marker_position = $marker_position;
5372 if ($closest_marker_position >= 0) {
5373 $text_marker = substr ($text, $closest_marker_position);
5376 if ($text_marker eq "") {
5382 $markup .= substr ($text, 0, $closest_marker_position);
5383 $text = substr ($text, $closest_marker_position);
5384 @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5386 if ($closest_marker eq "![" || $closest_marker eq "[") {
5389 if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5392 %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5395 $offset = length ($&);
5396 if ($element{"!"}) {
5400 $remaining_text = substr ($text, $offset);
5401 if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5406 $offset += length ($&);
5407 } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5408 $element{"ref"} = $1;
5409 $offset += length ($&);
5416 if ($element{"»"}) {
5417 $element{"»"} =~ s/&/&/g;
5418 $element{"»"} =~ s/</</g;
5420 if ($element{"!"}) {
5421 $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5423 if (defined ($element{"a"})) {
5424 $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5427 $markup .= "</inlinemediaobject>";
5428 } elsif ($element{"ref"}) {
5429 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5430 $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5432 if (defined ($element{"#"})) {
5433 # title attribute not supported
5436 $markup .= ">" . $element{"a"} . "</link>";
5438 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5439 $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5441 if (defined ($element{"#"})) {
5442 # title attribute not supported
5445 $markup .= ">" . $element{"a"} . "</ulink>";
5448 $markup .= $closest_marker;
5449 if ($closest_marker eq "![") {
5455 } elsif ($closest_marker eq "<") {
5456 if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5457 my $element_url = $1;
5458 $element_url =~ s/&/&/g;
5459 $element_url =~ s/</</g;
5461 $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5462 $offset = length ($&);
5463 } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5464 $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5465 $offset = length ($&);
5466 } elsif ($text =~ /^<[^>]+?>/) {
5468 $offset = length ($&);
5473 } elsif ($closest_marker eq "\\") {
5474 my $special_char = substr ($text, 1, 1);
5475 if ($MD_ESCAPABLE_CHARS{$special_char} ||
5476 $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5477 $markup .= $special_char;
5483 } elsif ($closest_marker eq "`") {
5484 if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5485 my $element_text = $2;
5486 $markup .= "<literal>" . $element_text . "</literal>";
5487 $offset = length ($&);
5492 } elsif ($closest_marker eq "@") {
5493 # Convert '@param()'
5494 # FIXME: we could make those also links ($symbol.$2), but that would be less
5495 # useful as the link target is a few lines up or down
5496 if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5497 $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5498 $offset = length ($&);
5499 } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5500 # Convert '@param', but not '\@param'.
5501 $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5502 $offset = length ($&);
5503 } elsif ($text =~ /^\\\@/) {
5505 $offset = length ($&);
5510 } elsif ($closest_marker eq "#") {
5511 if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5512 # handle #Object.func()
5513 $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5514 $offset = length ($&);
5515 } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5516 # Convert '#symbol', but not '\#symbol'.
5517 $markup .= $1 . &MakeHashXRef ($2, "type");
5518 $offset = length ($&);
5519 } elsif ($text =~ /^\\#/) {
5521 $offset = length ($&);
5526 } elsif ($closest_marker eq "%") {
5527 if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5528 # Convert '%constant', but not '\%constant'.
5529 # Also allow negative numbers, e.g. %-1.
5530 $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5531 $offset = length ($&);
5532 } elsif ($text =~ /^\\%/) {
5534 $offset = length ($&);
5542 $text = substr ($text, $offset);
5549 sub MarkDownParseSpanElements {
5551 my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5553 $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5555 # Convert 'function()' or 'macro()'.
5556 # if there is abc_*_def() we don't want to make a link to _def()
5557 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5558 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5563 sub ReplaceEntities {
5564 my ($text, $symbol) = @_;
5566 my @entities = ( [ "<", "<" ],
5575 [ "&", "&" ] ); # Do this last, or the others get messed up.
5578 # Expand entities in <programlisting> even inside CDATA since
5579 # we changed the definition of |[ to add CDATA
5580 for ($i = 0; $i <= $#entities; $i++) {
5581 $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5587 sub MarkDownOutputDocBook {
5588 my ($blocksref, $symbol, $context) = @_;
5591 my @blocks = @$blocksref;
5593 foreach $block (@blocks) {
5597 #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
5599 if ($block->{"type"} eq "paragraph") {
5600 $text = &MarkDownParseSpanElements ($block->{"text"});
5601 if ($context eq "li" && $output eq "") {
5602 if ($block->{"interrupted"}) {
5603 $output .= "\n<para>$text</para>\n";
5605 $output .= "<para>$text</para>";
5611 $output .= "<para>$text</para>\n";
5614 } elsif ($block->{"type"} eq "heading") {
5617 $title = &MarkDownParseSpanElements ($block->{"text"});
5619 if ($block->{"level"} == 1) {
5625 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5626 if (defined ($block->{"id"})) {
5627 $output .= "<$tag id=\"" . $block->{"id"} . "\">";
5629 $output .= "<$tag>";
5632 $output .= "<title>$title</title>$text</$tag>\n";
5633 } elsif ($block->{"type"} eq "li") {
5634 my $tag = "itemizedlist";
5636 if ($block->{"first"}) {
5637 if ($block->{"ordered"}) {
5638 $tag = "orderedlist";
5640 $output .= "<$tag>\n";
5643 if ($block->{"interrupted"}) {
5644 push @{$block->{"lines"}}, "";
5647 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5648 $output .= "<listitem>".$text."</listitem>\n";
5649 if ($block->{"last"}) {
5650 if ($block->{"ordered"}) {
5651 $tag = "orderedlist";
5653 $output .= "</$tag>\n";
5655 } elsif ($block->{"type"} eq "quote") {
5656 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5657 $output .= "<blockquote>\n$text</blockquote>\n";
5658 } elsif ($block->{"type"} eq "code") {
5659 my $tag = "programlisting";
5661 if ($block->{"language"}) {
5662 if ($block->{"language"} eq "plain") {
5663 $output .= "<informalexample><screen><![CDATA[\n";
5666 $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5669 $output .= "<informalexample><programlisting><![CDATA[\n";
5671 foreach (@{$block->{"lines"}}) {
5672 $output .= &ReplaceEntities ($_, $symbol) . "\n";
5674 $output .= "]]></$tag></informalexample>\n";
5675 } elsif ($block->{"type"} eq "markup") {
5676 $text = &ExpandAbbreviations($symbol, $block->{"text"});
5677 $output .= $text."\n";
5679 $output .= $block->{"text"}."\n";
5681 #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
5687 sub MarkDownParseLines {
5688 my ($linesref, $symbol, $context) = @_;
5690 my @lines = @$linesref;
5693 @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5694 $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5700 my ($text, $symbol) = @_;
5703 # take out some variability in line endings
5704 $text =~ s%\r\n%\n%g;
5708 @lines = split("\n", $text);
5709 $text = MarkDownParseLines(\@lines, $symbol, "");
5714 #############################################################################
5715 # Function : ReadDeclarationsFile
5716 # Description : This reads in a file containing the function/macro/enum etc.
5719 # Note that in some cases there are several declarations with
5720 # the same name, e.g. for conditional macros. In this case we
5721 # set a flag in the %DeclarationConditional hash so the
5722 # declaration is not shown in the docs.
5724 # If a macro and a function have the same name, e.g. for
5725 # gtk_object_ref, the function declaration takes precedence.
5727 # Some opaque structs are just declared with 'typedef struct
5728 # _name name;' in which case the declaration may be empty.
5729 # The structure may have been found later in the header, so
5730 # that overrides the empty declaration.
5732 # Arguments : $file - the declarations file to read
5733 # $override - if declarations in this file should override
5734 # any current declaration.
5735 #############################################################################
5737 sub ReadDeclarationsFile {
5738 my ($file, $override) = @_;
5740 if ($override == 0) {
5742 %DeclarationTypes = ();
5743 %DeclarationConditional = ();
5744 %DeclarationOutput = ();
5748 || die "Can't open $file: $!";
5749 my $declaration_type = "";
5750 my $declaration_name;
5752 my $is_deprecated = 0;
5754 if (!$declaration_type) {
5755 if (m/^<([^>]+)>/) {
5756 $declaration_type = $1;
5757 $declaration_name = "";
5758 @TRACE@("Found declaration: $declaration_type\n");
5762 if (m%^<NAME>(.*)</NAME>%) {
5763 $declaration_name = $1;
5764 } elsif (m%^<DEPRECATED/>%) {
5766 } elsif (m%^</$declaration_type>%) {
5767 @TRACE@("Found end of declaration: $declaration_name\n");
5768 # Check that the declaration has a name
5769 if ($declaration_name eq "") {
5770 &LogWarning ($file, $., "$declaration_type has no name.\n");
5773 # If the declaration is an empty typedef struct _XXX XXX
5774 # set the flag to indicate the struct has a typedef.
5775 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5776 && $declaration =~ m/^\s*$/) {
5777 @TRACE@("Struct has typedef: $declaration_name\n");
5778 $StructHasTypedef{$declaration_name} = 1;
5781 # Check if the symbol is already defined.
5782 if (defined ($Declarations{$declaration_name})
5783 && $override == 0) {
5784 # Function declarations take precedence.
5785 if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5787 } elsif ($declaration_type eq 'FUNCTION') {
5788 if ($is_deprecated) {
5789 $Deprecated{$declaration_name} = "";
5791 $Declarations{$declaration_name} = $declaration;
5792 $DeclarationTypes{$declaration_name} = $declaration_type;
5793 } elsif ($DeclarationTypes{$declaration_name}
5794 eq $declaration_type) {
5795 # If the existing declaration is empty, or is just a
5796 # forward declaration of a struct, override it.
5797 if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5798 if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5799 if ($is_deprecated) {
5800 $Deprecated{$declaration_name} = "";
5802 $Declarations{$declaration_name} = $declaration;
5803 } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5804 # Ignore an empty or forward declaration.
5806 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5809 # set flag in %DeclarationConditional hash for
5810 # multiply defined macros/typedefs.
5811 $DeclarationConditional{$declaration_name} = 1;
5814 &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5817 if ($is_deprecated) {
5818 $Deprecated{$declaration_name} = "";
5820 $Declarations{$declaration_name} = $declaration;
5821 $DeclarationTypes{$declaration_name} = $declaration_type;
5824 $declaration_type = "";
5835 #############################################################################
5836 # Function : ReadSignalsFile
5837 # Description : This reads in an existing file which contains information on
5838 # all GTK signals. It creates the arrays @SignalNames and
5839 # @SignalPrototypes containing info on the signals. The first
5840 # line of the SignalPrototype is the return type of the signal
5841 # handler. The remaining lines are the parameters passed to it.
5842 # The last parameter, "gpointer user_data" is always the same
5843 # so is not included.
5844 # Arguments : $file - the file containing the signal handler prototype
5846 #############################################################################
5848 sub ReadSignalsFile {
5856 my $signal_prototype;
5858 # Reset the signal info.
5859 @SignalObjects = ();
5861 @SignalReturns = ();
5863 @SignalPrototypes = ();
5868 if (!open (INPUT, $file)) {
5869 warn "Can't open $file - skipping signals\n";
5876 $signal_object = "";
5878 $signal_returns = "";
5879 $signal_prototype = "";
5882 if (m/^<NAME>(.*)<\/NAME>/) {
5884 if ($signal_name =~ m/^(.*)::(.*)$/) {
5885 $signal_object = $1;
5886 ($signal_name = $2) =~ s/_/-/g;
5887 @TRACE@("Found signal: $signal_name\n");
5889 &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5891 } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5892 $signal_returns = $1;
5893 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5895 } elsif (m%^</SIGNAL>%) {
5896 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5897 push (@SignalObjects, $signal_object);
5898 push (@SignalNames, $signal_name);
5899 push (@SignalReturns, $signal_returns);
5900 push (@SignalFlags, $signal_flags);
5901 push (@SignalPrototypes, $signal_prototype);
5904 $signal_prototype .= $_;
5912 #############################################################################
5913 # Function : ReadTemplateFile
5914 # Description : This reads in the manually-edited documentation file
5915 # corresponding to the file currently being created, so we can
5916 # insert the documentation at the appropriate places.
5917 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5918 # is a hash of arrays.
5919 # Arguments : $docsfile - the template file to read in.
5920 # $skip_unused_params - 1 if the unused parameters should be
5922 #############################################################################
5924 sub ReadTemplateFile {
5925 my ($docsfile, $skip_unused_params) = @_;
5927 my $template = "$docsfile.sgml";
5928 if (! -f $template) {
5929 @TRACE@("File doesn't exist: $template\n");
5933 # start with empty hashes, we merge the source comment for each file
5939 my $current_type = ""; # Type of symbol being read.
5940 my $current_symbol = ""; # Name of symbol being read.
5941 my $symbol_doc = ""; # Description of symbol being read.
5942 my @params; # Parameter names and descriptions of current
5943 # function/macro/function typedef.
5944 my $current_param = -1; # Index of parameter currently being read.
5945 # Note that the param array contains pairs
5946 # of param name & description.
5947 my $in_unused_params = 0; # True if we are reading in the unused params.
5948 my $in_deprecated = 0;
5950 my $in_stability = 0;
5952 open (DOCS, "$template")
5953 || die "Can't open $template: $!";
5955 @TRACE@("reading template $template");
5958 if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5961 if ($symbol eq "Title"
5962 || $symbol eq "Short_Description"
5963 || $symbol eq "Long_Description"
5964 || $symbol eq "See_Also"
5965 || $symbol eq "Stability_Level"
5966 || $symbol eq "Include"
5967 || $symbol eq "Image") {
5969 $symbol = $docsfile . ":" . $symbol;
5972 @TRACE@("Found symbol: $symbol\n");
5973 # Remember file and line for the symbol
5974 $SymbolSourceFile{$symbol} = $template;
5975 $SymbolSourceLine{$symbol} = $.;
5977 # Store previous symbol, but remove any trailing blank lines.
5978 if ($current_symbol ne "") {
5979 $symbol_doc =~ s/\s+$//;
5980 $SymbolTypes{$current_symbol} = $current_type;
5981 $SymbolDocs{$current_symbol} = $symbol_doc;
5983 # Check that the stability level is valid.
5984 if ($StabilityLevel{$current_symbol}) {
5985 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5988 if ($current_param >= 0) {
5989 $SymbolParams{$current_symbol} = [ @params ];
5991 # Delete any existing params in case we are overriding a
5992 # previously read template.
5993 delete $SymbolParams{$current_symbol};
5996 $current_type = $type;
5997 $current_symbol = $symbol;
5998 $current_param = -1;
5999 $in_unused_params = 0;
6006 } elsif (m/^<!-- # Unused Parameters # -->/) {
6007 @TRACE@("Found unused parameters\n");
6008 $in_unused_params = 1;
6011 } elsif ($in_unused_params && $skip_unused_params) {
6012 # When outputting the DocBook we skip unused parameters.
6013 @TRACE@("Skipping unused param: $_");
6017 # Check if param found. Need to handle "..." and "format...".
6018 if (s/^\@([\w\.]+):\040?//) {
6019 my $param_name = $1;
6020 my $param_desc = $_;
6021 # Allow variations of 'Returns'
6022 if ($param_name =~ m/^[Rr]eturns?$/) {
6023 $param_name = "Returns";
6025 # Allow varargs variations
6026 if ($param_name =~ m/^.*\.\.\.$/) {
6027 $param_name = "...";
6030 # strip trailing whitespaces and blank lines
6033 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
6035 if ($param_name eq "Deprecated") {
6037 $Deprecated{$current_symbol} = $_;
6038 } elsif ($param_name eq "Since") {
6041 $Since{$current_symbol} = $_;
6042 } elsif ($param_name eq "Stability") {
6044 $StabilityLevel{$current_symbol} = $_;
6046 push (@params, $param_name);
6047 push (@params, $param_desc);
6048 $current_param += $PARAM_FIELD_COUNT;
6051 # strip trailing whitespaces and blank lines
6056 if ($in_deprecated) {
6057 $Deprecated{$current_symbol} .= $_;
6058 } elsif ($in_since) {
6059 &LogWarning ($template, $., "multi-line since docs found");
6060 #$Since{$current_symbol} .= $_;
6061 } elsif ($in_stability) {
6062 $StabilityLevel{$current_symbol} .= $_;
6063 } elsif ($current_param >= 0) {
6064 $params[$current_param] .= $_;
6073 # Remember to finish the current symbol doccs.
6074 if ($current_symbol ne "") {
6076 $symbol_doc =~ s/\s+$//;
6077 $SymbolTypes{$current_symbol} = $current_type;
6078 $SymbolDocs{$current_symbol} = $symbol_doc;
6080 # Check that the stability level is valid.
6081 if ($StabilityLevel{$current_symbol}) {
6082 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
6085 if ($current_param >= 0) {
6086 $SymbolParams{$current_symbol} = [ @params ];
6088 # Delete any existing params in case we are overriding a
6089 # previously read template.
6090 delete $SymbolParams{$current_symbol};
6099 #############################################################################
6100 # Function : ReadObjectHierarchy
6101 # Description : This reads in the $MODULE-hierarchy.txt file containing all
6102 # the GtkObject subclasses described in this module (and their
6104 # It places them in the @Objects array, and places their level
6105 # in the object hierarchy in the @ObjectLevels array, at the
6106 # same index. GtkObject, the root object, has a level of 1.
6108 # This also generates tree_index.sgml as it goes along.
6111 #############################################################################
6113 sub ReadObjectHierarchy {
6117 if (! -f $OBJECT_TREE_FILE) {
6120 if (!open (INPUT, $OBJECT_TREE_FILE)) {
6121 warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
6125 # Only emit objects if they are supposed to be documented, or if
6126 # they have documented children. To implement this, we maintain a
6127 # stack of pending objects which will be emitted if a documented
6129 my @pending_objects = ();
6130 my @pending_levels = ();
6136 my $level = (length($`)) / 2 + 1;
6143 while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
6144 my $pobject = pop(@pending_objects);
6145 my $plevel = pop(@pending_levels);
6148 push (@pending_objects, $object);
6149 push (@pending_levels, $level);
6151 if (exists($KnownSymbols{$object})) {
6152 while ($#pending_levels >= 0) {
6153 $object = shift @pending_objects;
6154 $level = shift @pending_levels;
6155 $xref = &MakeXRef ($object);
6157 push (@tree, ' ' x ($level * 4) . "$xref");
6158 push (@Objects, $object);
6159 push (@ObjectLevels, $level);
6160 $ObjectRoots{$object} = $root;
6164 # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
6171 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
6172 my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
6173 my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
6175 open (OUTPUT, ">$new_tree_index")
6176 || die "Can't create $new_tree_index: $!";
6178 print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
6181 &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
6186 #############################################################################
6187 # Function : ReadInterfaces
6188 # Description : This reads in the $MODULE.interfaces file.
6191 #############################################################################
6193 sub ReadInterfaces {
6196 if (! -f $INTERFACES_FILE) {
6199 if (!open (INPUT, $INTERFACES_FILE)) {
6200 warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
6206 my ($object, @ifaces) = split;
6207 if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
6208 my @knownIfaces = ();
6210 # filter out private interfaces, but leave foreign interfaces
6211 foreach my $iface (@ifaces) {
6212 if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
6213 push (@knownIfaces, $iface);
6217 $Interfaces{$object} = join(' ', @knownIfaces);
6218 @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
6220 @TRACE@("skipping interfaces for unknown symbol: $object\n");
6226 #############################################################################
6227 # Function : ReadPrerequisites
6228 # Description : This reads in the $MODULE.prerequisites file.
6231 #############################################################################
6233 sub ReadPrerequisites {
6234 %Prerequisites = ();
6236 if (! -f $PREREQUISITES_FILE) {
6239 if (!open (INPUT, $PREREQUISITES_FILE)) {
6240 warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
6246 my ($iface, @prereqs) = split;
6247 if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
6248 my @knownPrereqs = ();
6250 # filter out private prerequisites, but leave foreign prerequisites
6251 foreach my $prereq (@prereqs) {
6252 if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
6253 push (@knownPrereqs, $prereq);
6257 $Prerequisites{$iface} = join(' ', @knownPrereqs);
6263 #############################################################################
6264 # Function : ReadArgsFile
6265 # Description : This reads in an existing file which contains information on
6266 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
6267 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
6269 # Arguments : $file - the file containing the arg information.
6270 #############################################################################
6285 # Reset the args info.
6298 if (!open (INPUT, $file)) {
6299 warn "Can't open $file - skipping args\n";
6316 if (m/^<NAME>(.*)<\/NAME>/) {
6318 if ($arg_name =~ m/^(.*)::(.*)$/) {
6320 ($arg_name = $2) =~ s/_/-/g;
6321 @TRACE@("Found arg: $arg_name\n");
6323 &LogWarning ($file, $., "Invalid argument name: $arg_name");
6325 } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6327 } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6329 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6331 } elsif (m/^<NICK>(.*)<\/NICK>/) {
6333 } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6335 if ($arg_blurb eq "(null)") {
6337 &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6339 } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6341 } elsif (m%^</ARG>%) {
6342 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6343 push (@ArgObjects, $arg_object);
6344 push (@ArgNames, $arg_name);
6345 push (@ArgTypes, $arg_type);
6346 push (@ArgRanges, $arg_range);
6347 push (@ArgFlags, $arg_flags);
6348 push (@ArgNicks, $arg_nick);
6349 push (@ArgBlurbs, $arg_blurb);
6350 push (@ArgDefaults, $arg_default);
6358 #############################################################################
6359 # Function : AddTreeLineArt
6360 # Description : Add unicode lineart to a pre-indented string array and returns
6361 # it as as multiline string.
6362 # Arguments : @tree - array of indented strings.
6363 #############################################################################
6365 sub AddTreeLineArt {
6366 my @tree = @{$_[0]};
6371 # iterate bottom up over the tree
6372 for ($i = $#tree; $i >= 0; $i--) {
6373 # count leading spaces
6374 $tree[$i] =~ /^([^<A-Za-z]*)/;
6375 $indent = length( $1 );
6376 # replace with ╰───, if place of ╰ is not space insert ├
6378 if (substr($tree[$i],$indent-4,1) eq " ") {
6379 substr($tree[$i],$indent-4,4) = "--- ";
6381 substr($tree[$i],$indent-4,4) = "+-- ";
6383 # go lines up while space and insert |
6384 for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6385 substr($tree[$j],$indent-4,1) = '|';
6390 my $res = join("\n", @tree);
6391 # unicode chars for: ╰──
6392 $res =~ s%---%<phrase role=\"lineart\">╰──</phrase>%g;
6393 # unicde chars for: ├──
6394 $res =~ s%\+--%<phrase role=\"lineart\">├──</phrase>%g;
6395 # unicode char for: │
6396 $res =~ s%\|%<phrase role=\"lineart\">│</phrase>%g;
6402 #############################################################################
6403 # Function : CheckIsObject
6404 # Description : Returns 1 if the given name is a GObject or a subclass.
6405 # It uses the global @Objects array.
6406 # Note that the @Objects array only contains classes in the
6407 # current module and their ancestors - not all GObject classes.
6408 # Arguments : $name - the name to check.
6409 #############################################################################
6413 my $root = $ObjectRoots{$name};
6414 # Let GBoxed pass as an object here to get -struct appended to the id
6415 # and prevent conflicts with sections.
6416 return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6420 #############################################################################
6421 # Function : MakeReturnField
6422 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6423 # Arguments : $str - the string to pad.
6424 #############################################################################
6426 sub MakeReturnField {
6429 return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6432 #############################################################################
6433 # Function : GetSymbolSourceFile
6434 # Description : Get the filename where the symbol docs where taken from.
6435 # Arguments : $symbol - the symbol name
6436 #############################################################################
6438 sub GetSymbolSourceFile {
6441 if (defined($SourceSymbolSourceFile{$symbol})) {
6442 return $SourceSymbolSourceFile{$symbol};
6443 } elsif (defined($SymbolSourceFile{$symbol})) {
6444 return $SymbolSourceFile{$symbol};
6450 #############################################################################
6451 # Function : GetSymbolSourceLine
6452 # Description : Get the file line where the symbol docs where taken from.
6453 # Arguments : $symbol - the symbol name
6454 #############################################################################
6456 sub GetSymbolSourceLine {
6459 if (defined($SourceSymbolSourceLine{$symbol})) {
6460 return $SourceSymbolSourceLine{$symbol};
6461 } elsif (defined($SymbolSourceLine{$symbol})) {
6462 return $SymbolSourceLine{$symbol};