4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998 Damon Chaplin
6 # 2007,2008,2009 Stefan Kost
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #############################################################################
24 # Script : gtkdoc-mkdb
25 # Description : This creates the DocBook files from the edited templates.
26 #############################################################################
32 push @INC, '@PACKAGE_DATA_DIR@';
33 require "gtkdoc-common.pl";
37 # name of documentation module
42 my $SOURCE_SUFFIXES = "";
43 my $IGNORE_FILES = "";
47 my $EXPAND_CONTENT_FILES = "";
48 my $INLINE_MARKUP_MODE;
49 my $DEFAULT_STABILITY;
53 my $OUTPUT_ALL_SYMBOLS;
54 my $OUTPUT_SYMBOLS_WITHOUT_SINCE;
56 my %optctl = ('module' => \$MODULE,
57 'source-dir' => \@SOURCE_DIRS,
58 'source-suffixes' => \$SOURCE_SUFFIXES,
59 'ignore-files' => \$IGNORE_FILES,
60 'output-dir' => \$SGML_OUTPUT_DIR,
61 'tmpl-dir' => \$TMPL_DIR,
62 'version' => \$PRINT_VERSION,
63 'help' => \$PRINT_HELP,
64 'main-sgml-file' => \$MAIN_SGML_FILE,
65 'expand-content-files' => \$EXPAND_CONTENT_FILES,
66 'sgml-mode' => \$INLINE_MARKUP_MODE,
67 'xml-mode' => \$INLINE_MARKUP_MODE,
68 'default-stability' => \$DEFAULT_STABILITY,
69 'default-includes' => \$DEFAULT_INCLUDES,
70 'output-format' => \$OUTPUT_FORMAT,
71 'name-space' => \$NAME_SPACE,
72 'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
73 'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
75 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
76 "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version", "outputallsymbols",
77 "outputsymbolswithoutsince",
78 "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
79 "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
80 "output-format:s", "name-space:s");
91 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
92 && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
98 gtkdoc-mkdb version @VERSION@ - generate docbook files
100 --module=MODULE_NAME Name of the doc module being parsed
101 --source-dir=DIRNAME Directories which contain inline reference material
102 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
103 --ignore-files=FILES Files or directories which should not be scanned
104 May be used more than once for multiple directories
105 --output-dir=DIRNAME Directory to put the generated DocBook files in
106 --tmpl-dir=DIRNAME Directory in which template files may be found
107 --main-sgml-file=FILE File containing the toplevel DocBook file.
108 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
109 --output-format=FORMAT Format to use for the generated docbook, XML or SGML.
110 --{xml,sgml}-mode Allow DocBook markup in inline documentation.
111 --default-stability=LEVEL Specify default stability Level. Valid values are
112 Stable, Unstable, or Private.
113 --default-includes=FILENAMES Specify default includes for section Synopsis
114 --name-space=NS Omit namespace in index.
115 --version Print the version of this program
116 --help Print this help
121 my ($empty_element_end, $doctype_header);
123 # autodetect output format
124 if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
125 if (!$MAIN_SGML_FILE) {
126 if (-e "${MODULE}-docs.xml") {
127 $OUTPUT_FORMAT = "xml";
129 $OUTPUT_FORMAT = "sgml";
132 if ($MAIN_SGML_FILE =~ m/.*\.(.*ml)$/i) {
133 $OUTPUT_FORMAT = lc($1);
138 $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
141 #print "DEBUG: output-format: [$OUTPUT_FORMAT]\n";
143 if ($OUTPUT_FORMAT eq "xml") {
144 if (!$MAIN_SGML_FILE) {
145 # backwards compatibility
146 if (-e "${MODULE}-docs.sgml") {
147 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
149 $MAIN_SGML_FILE = "${MODULE}-docs.xml";
152 $empty_element_end = "/>";
154 if (-e $MAIN_SGML_FILE) {
155 open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
156 $doctype_header = "";
158 if (/^\s*<(book|chapter|article)/) {
159 # check that the top-level tag or the doctype decl contain the xinclude namespace decl
160 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
161 $doctype_header = "";
165 $doctype_header .= $_;
168 $doctype_header =~ s/<!DOCTYPE \w+/<!DOCTYPE refentry/;
169 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
170 # FIXME: not sure if we can do this now, as people already work-around the problem
171 # $doctype_header =~ s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#g;
174 "<?xml version=\"1.0\"?>\n" .
175 "<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.3//EN\"\n" .
176 " \"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd\"\n" .
178 " <!ENTITY % local.common.attrib \"xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'\">\n" .
182 if (!$MAIN_SGML_FILE) {
183 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
185 $empty_element_end = ">";
186 $doctype_header = "";
191 # All the files are written in subdirectories beneath here.
192 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
194 # This is where we put all the DocBook output.
195 $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/$OUTPUT_FORMAT";
197 # This file contains the object hierarchy.
198 my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
200 # This file contains the interfaces.
201 my $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
203 # This file contains the prerequisites.
204 my $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
206 # This file contains signal arguments and names.
207 my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
209 # The file containing Arg information.
210 my $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
212 # These global arrays store information on signals. Each signal has an entry
213 # in each of these arrays at the same index, like a multi-dimensional array.
214 my @SignalObjects; # The GtkObject which emits the signal.
215 my @SignalNames; # The signal name.
216 my @SignalReturns; # The return type.
217 my @SignalFlags; # Flags for the signal
218 my @SignalPrototypes; # The rest of the prototype of the signal handler.
220 # These global arrays store information on Args. Each Arg has an entry
221 # in each of these arrays at the same index, like a multi-dimensional array.
222 my @ArgObjects; # The GtkObject which has the Arg.
223 my @ArgNames; # The Arg name.
224 my @ArgTypes; # The Arg type - gint, GtkArrowType etc.
225 my @ArgFlags; # How the Arg can be used - readable/writable etc.
226 my @ArgNicks; # The nickname of the Arg.
227 my @ArgBlurbs; # Docstring of the Arg.
228 my @ArgDefaults; # Default value of the Arg.
229 my @ArgRanges; # The range of the Arg type
230 # These global hashes store declaration info keyed on a symbol name.
232 my %DeclarationTypes;
233 my %DeclarationConditional;
234 my %DeclarationOutput;
238 my %StructHasTypedef;
240 # These global hashes store the existing documentation.
244 my %SymbolSourceFile;
245 my %SymbolSourceLine;
247 # These global hashes store documentation scanned from the source files.
248 my %SourceSymbolDocs;
249 my %SourceSymbolParams;
250 my %SourceSymbolSourceFile;
251 my %SourceSymbolSourceLine;
253 # all documentation goes in here, so we can do coverage analysis
255 my %AllIncompleteSymbols;
256 my %AllUnusedSymbols;
257 my %AllDocumentedSymbols;
259 # Undeclared yet documented symbols
260 my %UndeclaredSymbols;
262 # These global arrays store GObject, subclasses and the hierarchy (also of
263 # non-object derived types).
271 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
272 # section they are defined
277 # collects index entries
278 my %IndexEntriesFull;
279 my %IndexEntriesSince;
280 my %IndexEntriesDeprecated;
282 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
283 my %PreProcessorDirectives;
284 $PreProcessorDirectives{"assert"} = 1;
285 $PreProcessorDirectives{"define"} = 1;
286 $PreProcessorDirectives{"elif"} = 1;
287 $PreProcessorDirectives{"else"} = 1;
288 $PreProcessorDirectives{"endif"} = 1;
289 $PreProcessorDirectives{"error"} = 1;
290 $PreProcessorDirectives{"if"} = 1;
291 $PreProcessorDirectives{"ifdef"} = 1;
292 $PreProcessorDirectives{"ifndef"} = 1;
293 $PreProcessorDirectives{"include"} = 1;
294 $PreProcessorDirectives{"line"} = 1;
295 $PreProcessorDirectives{"pragma"} = 1;
296 $PreProcessorDirectives{"unassert"} = 1;
297 $PreProcessorDirectives{"undef"} = 1;
298 $PreProcessorDirectives{"warning"} = 1;
300 # remember used annotation (to write minimal glossary)
303 my %AnnotationDefinition = (
304 # the GObjectIntrospection annotations are defined at:
305 # https://live.gnome.org/GObjectIntrospection/Annotations
306 'allow-none' => "NULL is ok, both for passing and for returning.",
307 'array' => "Parameter points to an array of items.",
308 'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
309 'attributes' => "Free-form key-value pairs.",
310 'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
311 'constructor' => "This symbol is a constructor, not a static method.",
312 'destroy' => "This parameter is a 'destroy_data', for callbacks.",
313 'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
314 'element-type' => "Generics and defining elements of containers and arrays.",
315 'error-domains' => "Typed errors. Similar to throws in Java.",
316 'foreign' => "This is a foreign struct.",
317 'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
318 'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
319 'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
320 'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
321 'method' => "This is a method",
322 'not-error' => "A GError parameter is not to be handled like a normal GError.",
323 'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
324 'out caller-allocates' => "Out parameter, where caller must allocate storage.",
325 'out callee-allocates' => "Out parameter, where caller must allocate storage.",
326 'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
327 'rename-to' => "Rename the original symbol's name to SYMBOL.",
328 'scope call' => "The callback is valid only during the call to the method.",
329 'scope async' => "The callback is valid until first called.",
330 'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
331 'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
332 'skip' => "Exposed in C code, not necessarily available in other languages.",
333 'transfer container' => "Free data container after the code is done.",
334 'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
335 'transfer full' => "Free data after the code is done.",
336 'transfer none' => "Don't free data after the code is done.",
337 'type' => "Override the parsed C type with given type.",
338 'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
339 'virtual' => "This is the invoker for a virtual method.",
340 'value' => "The specified value overrides the evaluated value of the constant.",
341 # Stability Level definition
342 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
344 The intention of a Stable interface is to enable arbitrary third parties to
345 develop applications to these interfaces, release them, and have confidence that
346 they will run on all minor releases of the product (after the one in which the
347 interface was introduced, and within the same major release). Even at a major
348 release, incompatible changes are expected to be rare, and to have strong
352 Unstable interfaces are experimental or transitional. They are typically used to
353 give outside developers early access to new or rapidly changing technology, or
354 to provide an interim solution to a problem where a more general solution is
355 anticipated. No claims are made about either source or binary compatibility from
356 one minor release to the next.
358 The Unstable interface level is a warning that these interfaces are subject to
359 change without warning and should not be used in unbundled products.
361 Given such caveats, customer impact need not be a factor when considering
362 incompatible changes to an Unstable interface in a major or minor release.
363 Nonetheless, when such changes are introduced, the changes should still be
364 mentioned in the release notes for the affected release.
367 An interface that can be used within the GNOME stack itself, but that is not
368 documented for end-users. Such functions should only be used in specified and
373 # Elements to consider non-block items in MarkDown parsing
374 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
386 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
402 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
405 # Create the root DocBook output directory if it doens't exist.
406 if (! -e $SGML_OUTPUT_DIR) {
407 mkdir ("$SGML_OUTPUT_DIR", 0777)
408 || die "Can't create directory: $SGML_OUTPUT_DIR";
411 # Function and other declaration output settings.
412 my $RETURN_TYPE_FIELD_WIDTH = 20;
413 my $SYMBOL_FIELD_WIDTH = 36;
414 my $MAX_SYMBOL_FIELD_WIDTH = 40;
415 my $SIGNAL_FIELD_WIDTH = 16;
416 my $PARAM_FIELD_COUNT = 2;
418 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
419 &ReadSignalsFile ($SIGNALS_FILE);
420 &ReadArgsFile ($ARGS_FILE);
421 &ReadObjectHierarchy;
425 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
426 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
427 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
430 for my $dir (@SOURCE_DIRS) {
431 &ReadSourceDocumentation ($dir);
434 my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt");
436 # If any of the DocBook SGML files have changed, update the timestamp file (so
437 # it can be used for Makefile dependencies).
438 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
440 # try to detect the common prefix
441 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
442 if ($NAME_SPACE eq "") {
449 foreach my $symbol (keys(%IndexEntriesFull)) {
450 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
451 if (length($symbol)>$pos) {
452 $letter=substr($symbol,$pos,1);
453 # stop prefix scanning
454 if ($letter eq "_") {
458 # Should we also stop on a uppercase char, if last was lowercase
459 # GtkWidget, if we have the 'W' and had the 't' before
460 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
461 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
462 # need to recound each time as this is per symbol
463 $prefix{uc($letter)}++;
467 if ($letter ne "" && $letter ne "_") {
470 foreach $letter (keys(%prefix)) {
471 #print "$letter: $prefix{$letter}.\n";
472 if ($prefix{$letter}>$maxsymbols) {
474 $maxsymbols=$prefix{$letter};
477 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
478 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
481 $NAME_SPACE .= $maxletter;
488 } while ($ratio > 0.9);
489 #print "most symbols start with $NAME_SPACE\n";
493 &OutputDeprecatedIndex;
495 &OutputAnnotationGlossary;
497 open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
498 || die "Can't create $ROOT_DIR/sgml.stamp: $!";
499 print (TIMESTAMP "timestamp");
503 #############################################################################
504 # Function : OutputObjectList
505 # Description : This outputs the alphabetical list of objects, in a columned
507 # FIXME: Currently this also outputs ancestor objects
508 # which may not actually be in this module.
510 #############################################################################
512 sub OutputObjectList {
515 # FIXME: use $OUTPUT_FORMAT
516 # my $old_object_index = "$SGML_OUTPUT_DIR/object_index.$OUTPUT_FORMAT";
517 my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml";
518 my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new";
520 open (OUTPUT, ">$new_object_index")
521 || die "Can't create $new_object_index: $!";
523 if ($OUTPUT_FORMAT eq "xml") {
524 my $header = $doctype_header;
526 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE informaltable/;
527 print (OUTPUT "$header");
530 print (OUTPUT <<EOF);
531 <informaltable pgwide="1" frame="none">
532 <tgroup cols="$cols">
533 <colspec colwidth="1*"${empty_element_end}
534 <colspec colwidth="1*"${empty_element_end}
535 <colspec colwidth="1*"${empty_element_end}
541 foreach $object (sort (@Objects)) {
542 my $xref = &MakeXRef ($object);
543 if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
544 print (OUTPUT "<entry>$xref</entry>\n");
545 if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
549 # emit an empty row, since empty tables are invalid
550 print (OUTPUT "<row><entry> </entry></row>\n");
553 if ($count % $cols > 0) {
554 print (OUTPUT "</row>\n");
558 print (OUTPUT <<EOF);
559 </tbody></tgroup></informaltable>
563 &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
566 #############################################################################
567 # Function : TrimTextBlock
568 # Description : Trims extra whitespace. Empty lines inside a block are
570 # Arguments : $desc - the text block to trim. May contain newlines.
571 #############################################################################
576 # strip leading spaces on the block
578 # strip trailing spaces on every line
579 $desc =~ s/\s+$/\n/mg;
585 #############################################################################
586 # Function : OutputSGML
587 # Description : This collects the output for each section of the docs, and
588 # outputs each file when the end of the section is found.
589 # Arguments : $file - the $MODULE-sections.txt file which contains all of
590 # the functions/macros/structs etc. being documented, organised
591 # into sections and subsections.
592 #############################################################################
597 #print "Reading: $file\n";
599 || die "Can't open $file: $!";
602 my $book_bottom = "";
603 my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
604 my $section_includes = "";
611 my $functions_synop = "";
612 my $other_synop = "";
613 my $functions_details = "";
614 my $other_details = "";
615 my $signals_synop = "";
616 my $signals_desc = "";
618 my $child_args_synop = "";
619 my $style_args_synop = "";
621 my $child_args_desc = "";
622 my $style_args_desc = "";
623 my $hierarchy_str = "";
626 my $implementations = "";
627 my $prerequisites = "";
629 my @file_objects = ();
631 my %symbol_def_line = ();
633 # merge the source docs, in case there are no templates
634 &MergeSourceDocumentation;
640 } elsif (m/^<SECTION>/) {
644 %symbol_def_line = ();
646 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
647 $other_synop .= "\n";
648 $functions_synop .= "\n";
651 } elsif (m/^<SUBSECTION>/) {
653 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
655 #print "Section: $title\n";
657 # We don't want warnings if object & class structs aren't used.
658 $DeclarationOutput{$title} = 1;
659 $DeclarationOutput{"${title}Class"} = 1;
660 $DeclarationOutput{"${title}Iface"} = 1;
661 $DeclarationOutput{"${title}Interface"} = 1;
663 } elsif (m/^<FILE>(.*)<\/FILE>/) {
665 if (! defined $templates{$filename}) {
666 if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
667 &MergeSourceDocumentation;
668 $templates{$filename}=$.;
671 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
672 "Previous occurrence on line ".$templates{$filename}.".");
674 if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
675 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
676 # Remove trailing blanks
680 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
682 $section_includes = $1;
684 if (defined $DEFAULT_INCLUDES) {
685 &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
692 } elsif (m/^<\/SECTION>/) {
693 #print "End of section: $title\n";
694 if ($num_symbols > 0) {
696 if ($OUTPUT_FORMAT eq "xml") {
697 $book_bottom .= " <xi:include href=\"xml/$filename.xml\"/>\n";
699 $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n";
700 $book_bottom .= " &$section_id;\n";
703 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
704 if ($section_includes) {
705 &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
707 $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
709 if ($section_includes eq "") {
710 $section_includes = $includes;
713 $signals_synop =~ s/^\n*//g;
714 $signals_synop =~ s/\n+$/\n/g;
715 if ($signals_synop ne '') {
716 $signals_synop = <<EOF;
717 <refsect1 id="$section_id.signals" role="signal_proto">
718 <title role="signal_proto.title">Signals</title>
719 <informaltable frame="none">
721 <colspec colname="signals_return" colwidth="150px"/>
722 <colspec colname="signals_name" colwidth="300px"/>
723 <colspec colname="signals_flags" colwidth="200px"/>
731 $signals_desc = TrimTextBlock($signals_desc);
732 $signals_desc = <<EOF;
733 <refsect1 id="$section_id.signal-details" role="signals">
734 <title role="signals.title">Signal Details</title>
740 $args_synop =~ s/^\n*//g;
741 $args_synop =~ s/\n+$/\n/g;
742 if ($args_synop ne '') {
744 <refsect1 id="$section_id.properties" role="properties">
745 <title role="properties.title">Properties</title>
746 <informaltable frame="none">
748 <colspec colname="properties_type" colwidth="150px"/>
749 <colspec colname="properties_name" colwidth="300px"/>
750 <colspec colname="properties_flags" colwidth="200px"/>
758 $args_desc = TrimTextBlock($args_desc);
760 <refsect1 id="$section_id.property-details" role="property_details">
761 <title role="property_details.title">Property Details</title>
767 $child_args_synop =~ s/^\n*//g;
768 $child_args_synop =~ s/\n+$/\n/g;
769 if ($child_args_synop ne '') {
770 $args_synop .= <<EOF;
771 <refsect1 id="$section_id.child-properties" role="child_properties">
772 <title role="child_properties.title">Child Properties</title>
773 <informaltable frame="none">
775 <colspec colname="child_properties_type" colwidth="150px"/>
776 <colspec colname="child_properties_name" colwidth="300px"/>
777 <colspec colname="child_properties_flags" colwidth="200px"/>
785 $child_args_desc = TrimTextBlock($child_args_desc);
787 <refsect1 id="$section_id.child-property-details" role="child_property_details">
788 <title role="child_property_details.title">Child Property Details</title>
794 $style_args_synop =~ s/^\n*//g;
795 $style_args_synop =~ s/\n+$/\n/g;
796 if ($style_args_synop ne '') {
797 $args_synop .= <<EOF;
798 <refsect1 id="$section_id.style-properties" role="style_properties">
799 <title role="style_properties.title">Style Properties</title>
800 <informaltable frame="none">
802 <colspec colname="style_properties_type" colwidth="150px"/>
803 <colspec colname="style_properties_name" colwidth="300px"/>
804 <colspec colname="style_properties_flags" colwidth="200px"/>
812 $style_args_desc = TrimTextBlock($style_args_desc);
814 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
815 <title role="style_properties_details.title">Style Property Details</title>
821 $hierarchy_str = &AddTreeLineArt(\@hierarchy) . "\n";
822 if ($hierarchy_str ne "") {
823 $hierarchy_str = <<EOF;
824 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
825 <title role="object_hierarchy.title">Object Hierarchy</title>
826 <screen>$hierarchy_str</screen>
831 $interfaces =~ TrimTextBlock($interfaces);
832 if ($interfaces ne "") {
834 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
835 <title role="impl_interfaces.title">Implemented Interfaces</title>
841 $implementations = TrimTextBlock($implementations);
842 if ($implementations ne "") {
843 $implementations = <<EOF;
844 <refsect1 id="$section_id.implementations" role="implementations">
845 <title role="implementations.title">Known Implementations</title>
851 $prerequisites = TrimTextBlock($prerequisites);
852 if ($prerequisites ne "") {
853 $prerequisites = <<EOF;
854 <refsect1 id="$section_id.prerequisites" role="prerequisites">
855 <title role="prerequisites.title">Prerequisites</title>
861 $derived = TrimTextBlock($derived);
862 if ($derived ne "") {
864 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
865 <title role="derived_interfaces.title">Known Derived Interfaces</title>
871 $functions_synop =~ s/^\n*//g;
872 $functions_synop =~ s/\n+$/\n/g;
873 if ($functions_synop ne '') {
874 $functions_synop = <<EOF;
875 <refsect1 id="$section_id.functions" role="functions_proto">
876 <title role="functions_proto.title">Functions</title>
877 <informaltable pgwide="1" frame="none">
879 <colspec colname="functions_return" colwidth="150px"/>
880 <colspec colname="functions_name"/>
890 $other_synop =~ s/^\n*//g;
891 $other_synop =~ s/\n+$/\n/g;
892 if ($other_synop ne '') {
893 $other_synop = <<EOF;
894 <refsect1 id="$section_id.other" role="other_proto">
895 <title role="other_proto.title">Types and Values</title>
896 <informaltable role="enum_members_table" pgwide="1" frame="none">
898 <colspec colname="name" colwidth="150px"/>
899 <colspec colname="description"/>
909 my $file_changed = &OutputSGMLFile ($filename, $title, $section_id,
911 \$functions_synop, \$other_synop,
912 \$functions_details, \$other_details,
913 \$signals_synop, \$signals_desc,
914 \$args_synop, \$args_desc,
915 \$hierarchy_str, \$interfaces,
917 \$prerequisites, \$derived,
927 $section_includes = "";
928 $functions_synop = "";
930 $functions_details = "";
935 $child_args_synop = "";
936 $style_args_synop = "";
938 $child_args_desc = "";
939 $style_args_desc = "";
943 $implementations = "";
947 } elsif (m/^(\S+)/) {
949 @TRACE@(" Symbol: $symbol in subsection: $subsection\n");
951 # check for duplicate entries
952 if (! defined $symbol_def_line{$symbol}) {
953 my $declaration = $Declarations{$symbol};
954 if (defined ($declaration)) {
955 if (&CheckIsObject ($symbol)) {
956 push @file_objects, $symbol;
958 # We don't want standard macros/functions of GObjects,
959 # or private declarations.
960 if ($subsection ne "Standard" && $subsection ne "Private") {
961 my ($synop, $desc) = &OutputDeclaration ($symbol,
963 my $type = $DeclarationTypes {$symbol};
965 if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
966 $functions_synop .= $synop;
967 $functions_details .= $desc;
968 } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
969 $functions_synop .= $synop;
970 $functions_details .= $desc;
972 $other_synop .= $synop;
973 $other_details .= $desc;
976 my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
977 my ($arg_synop, $child_arg_synop, $style_arg_synop,
978 $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
979 my $ifaces = &GetInterfaces ($symbol);
980 my $impls = &GetImplementations ($symbol);
981 my $prereqs = &GetPrerequisites ($symbol);
982 my $der = &GetDerived ($symbol);
983 @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
985 $signals_synop .= $sig_synop;
986 $signals_desc .= $sig_desc;
987 $args_synop .= $arg_synop;
988 $child_args_synop .= $child_arg_synop;
989 $style_args_synop .= $style_arg_synop;
990 $args_desc .= $arg_desc;
991 $child_args_desc .= $child_arg_desc;
992 $style_args_desc .= $style_arg_desc;
993 $interfaces .= $ifaces;
994 $implementations .= $impls;
995 $prerequisites .= $prereqs;
998 # Note that the declaration has been output.
999 $DeclarationOutput{$symbol} = 1;
1000 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
1001 $UndeclaredSymbols{$symbol} = 1;
1002 &LogWarning ($file, $., "No declaration found for $symbol.");
1005 $symbol_def_line{$symbol}=$.;
1007 if ($section_id eq "") {
1008 if($title eq "" && $filename eq "") {
1009 &LogWarning ($file, $., "Section has no title and no file.");
1011 # FIXME: one of those would be enough
1012 # filename should be an internal detail for gtk-doc
1015 } elsif ($filename eq "") {
1018 $filename =~ s/\s/_/g;
1020 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1021 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1022 # Remove trailing blanks and use as is
1023 $section_id =~ s/\s+$//;
1024 } elsif (&CheckIsObject ($title)) {
1025 # GObjects use their class name as the ID.
1026 $section_id = &CreateValidSGMLID ($title);
1028 $section_id = &CreateValidSGMLID ("$MODULE-$title");
1031 $SymbolSection{$symbol}=$title;
1032 $SymbolSectionId{$symbol}=$section_id;
1035 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1036 "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1042 &OutputMissingDocumentation;
1043 &OutputUndeclaredSymbols;
1044 &OutputUnusedSymbols;
1046 if ($OUTPUT_ALL_SYMBOLS) {
1049 if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1050 &OutputSymbolsWithoutSince;
1053 for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1054 my $file_changed = &OutputExtraFile ($filename);
1055 if ($file_changed) {
1060 &OutputBook ($book_top, $book_bottom);
1065 #############################################################################
1066 # Function : OutputIndex
1067 # Description : This writes an indexlist that can be included into the main-
1068 # document into an <index> tag.
1069 #############################################################################
1072 my ($basename, $apiindexref ) = @_;
1073 my %apiindex = %{$apiindexref};
1074 my $old_index = "$SGML_OUTPUT_DIR/$basename.xml";
1075 my $new_index = "$SGML_OUTPUT_DIR/$basename.new";
1076 my $lastletter = " ";
1081 open (OUTPUT, ">$new_index")
1082 || die "Can't create $new_index";
1084 my $header = $doctype_header;
1085 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE indexdiv/;
1087 print (OUTPUT "$header<indexdiv>\n");
1089 #print "generate $basename index (".%apiindex." entries)\n";
1091 # do a case insensitive sort while chopping off the prefix
1093 sort { $$a{criteria} cmp $$b{criteria} }
1094 map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1097 $symbol = $$hash{original};
1098 if (defined($$hash{short})) {
1099 $short_symbol = $$hash{short};
1101 $short_symbol = $symbol;
1104 # generate a short symbol description
1105 my $symbol_desc = "";
1106 my $symbol_section = "";
1107 my $symbol_section_id = "";
1108 my $symbol_type = "";
1109 if (defined($DeclarationTypes{$symbol})) {
1110 $symbol_type = lc($DeclarationTypes{$symbol});
1112 if ($symbol_type eq "") {
1113 #print "trying symbol $symbol\n";
1114 if ($symbol =~ m/(.*)::(.*)/) {
1118 #print " trying object signal ${oname}:$osym in ".$#SignalNames." signals\n";
1119 for ($i = 0; $i <= $#SignalNames; $i++) {
1120 if ($SignalNames[$i] eq $osym) {
1121 $symbol_type = "object signal";
1122 if (defined($SymbolSection{$oname})) {
1123 $symbol_section = $SymbolSection{$oname};
1124 $symbol_section_id = $SymbolSectionId{$oname};
1129 } elsif ($symbol =~ m/(.*):(.*)/) {
1133 #print " trying object property ${oname}::$osym in ".$#ArgNames." properties\n";
1134 for ($i = 0; $i <= $#ArgNames; $i++) {
1135 #print " ".$ArgNames[$i]."\n";
1136 if ($ArgNames[$i] eq $osym) {
1137 $symbol_type = "object property";
1138 if (defined($SymbolSection{$oname})) {
1139 $symbol_section = $SymbolSection{$oname};
1140 $symbol_section_id = $SymbolSectionId{$oname};
1147 if (defined($SymbolSection{$symbol})) {
1148 $symbol_section = $SymbolSection{$symbol};
1149 $symbol_section_id = $SymbolSectionId{$symbol};
1152 if ($symbol_type ne "") {
1153 $symbol_desc=", $symbol_type";
1154 if ($symbol_section ne "") {
1155 $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1156 #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1160 my $curletter = uc(substr($short_symbol,0,1));
1161 my $id = $apiindex{$symbol};
1163 #print " add symbol $symbol with $id to index in section $curletter\n";
1165 if ($curletter ne $lastletter) {
1166 $lastletter = $curletter;
1168 if ($divopen == 1) {
1169 print (OUTPUT "</indexdiv>\n");
1171 print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1175 print (OUTPUT <<EOF);
1176 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1180 if ($divopen == 1) {
1181 print (OUTPUT "</indexdiv>\n");
1183 print (OUTPUT "</indexdiv>\n");
1186 &UpdateFileIfChanged ($old_index, $new_index, 0);
1190 #############################################################################
1191 # Function : OutputIndexFull
1192 # Description : This writes the full api indexlist that can be included into the
1193 # main document into an <index> tag.
1194 #############################################################################
1196 sub OutputIndexFull {
1197 &OutputIndex ("api-index-full", \%IndexEntriesFull);
1201 #############################################################################
1202 # Function : OutputDeprecatedIndex
1203 # Description : This writes the deprecated api indexlist that can be included
1204 # into the main document into an <index> tag.
1205 #############################################################################
1207 sub OutputDeprecatedIndex {
1208 &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1212 #############################################################################
1213 # Function : OutputSinceIndexes
1214 # Description : This writes the 'since' api indexlists that can be included into
1215 # the main document into an <index> tag.
1216 #############################################################################
1218 sub OutputSinceIndexes {
1219 my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1221 foreach my $version (@sinces) {
1222 #print "Since : [$version]\n";
1223 # TODO make filtered hash
1224 #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1225 my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1227 &OutputIndex ("api-index-$version", \%index);
1231 #############################################################################
1232 # Function : OutputAnnotationGlossary
1233 # Description : This writes a glossary of the used annotation terms into a
1234 # separate glossary file that can be included into the main
1236 #############################################################################
1238 sub OutputAnnotationGlossary {
1239 my $old_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.xml";
1240 my $new_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.new";
1241 my $lastletter = " ";
1244 # if there are no annotations used return
1245 return if (! keys(%AnnotationsUsed));
1247 # add acronyms that are referenced from acronym text
1249 foreach my $annotation (keys(%AnnotationsUsed)) {
1250 if(defined($AnnotationDefinition{$annotation})) {
1251 if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1252 if (!exists($AnnotationsUsed{$1})) {
1253 $AnnotationsUsed{$1} = 1;
1260 open (OUTPUT, ">$new_glossary")
1261 || die "Can't create $new_glossary";
1263 my $header = $doctype_header;
1264 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE glossary/;
1266 print (OUTPUT <<EOF);
1268 <glossary id="annotation-glossary">
1269 <title>Annotation Glossary</title>
1272 foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1273 if(defined($AnnotationDefinition{$annotation})) {
1274 my $def = $AnnotationDefinition{$annotation};
1275 my $curletter = uc(substr($annotation,0,1));
1277 if ($curletter ne $lastletter) {
1278 $lastletter = $curletter;
1280 if ($divopen == 1) {
1281 print (OUTPUT "</glossdiv>\n");
1283 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1286 print (OUTPUT <<EOF);
1288 <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1297 if ($divopen == 1) {
1298 print (OUTPUT "</glossdiv>\n");
1300 print (OUTPUT "</glossary>\n");
1303 &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1306 #############################################################################
1307 # Function : ReadKnownSymbols
1308 # Description : This collects the names of non-private symbols from the
1309 # $MODULE-sections.txt file.
1310 # Arguments : $file - the $MODULE-sections.txt file which contains all of
1311 # the functions/macros/structs etc. being documented, organised
1312 # into sections and subsections.
1313 #############################################################################
1315 sub ReadKnownSymbols {
1318 my $subsection = "";
1320 #print "Reading: $file\n";
1322 || die "Can't open $file: $!";
1328 } elsif (m/^<SECTION>/) {
1331 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1334 } elsif (m/^<SUBSECTION>/) {
1337 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1340 } elsif (m/^<FILE>(.*)<\/FILE>/) {
1341 $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1342 $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1345 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1348 } elsif (m/^<\/SECTION>/) {
1351 } elsif (m/^(\S+)/) {
1354 if ($subsection ne "Standard" && $subsection ne "Private") {
1355 $KnownSymbols{$symbol} = 1;
1358 $KnownSymbols{$symbol} = 0;
1366 #############################################################################
1367 # Function : OutputDeclaration
1368 # Description : Returns the synopsis and detailed description DocBook
1369 # describing one function/macro etc.
1370 # Arguments : $symbol - the name of the function/macro begin described.
1371 # $declaration - the declaration of the function/macro.
1372 #############################################################################
1374 sub OutputDeclaration {
1375 my ($symbol, $declaration) = @_;
1377 my $type = $DeclarationTypes {$symbol};
1378 if ($type eq 'MACRO') {
1379 return &OutputMacro ($symbol, $declaration);
1380 } elsif ($type eq 'TYPEDEF') {
1381 return &OutputTypedef ($symbol, $declaration);
1382 } elsif ($type eq 'STRUCT') {
1383 return &OutputStruct ($symbol, $declaration);
1384 } elsif ($type eq 'ENUM') {
1385 return &OutputEnum ($symbol, $declaration);
1386 } elsif ($type eq 'UNION') {
1387 return &OutputUnion ($symbol, $declaration);
1388 } elsif ($type eq 'VARIABLE') {
1389 return &OutputVariable ($symbol, $declaration);
1390 } elsif ($type eq 'FUNCTION') {
1391 return &OutputFunction ($symbol, $declaration, $type);
1392 } elsif ($type eq 'USER_FUNCTION') {
1393 return &OutputFunction ($symbol, $declaration, $type);
1395 die "Unknown symbol type";
1400 #############################################################################
1401 # Function : OutputSymbolTraits
1402 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1403 # Arguments : $symbol - the name of the function/macro begin described.
1404 #############################################################################
1406 sub OutputSymbolTraits {
1410 if (exists $Since{$symbol}) {
1411 $desc .= "<para role=\"since\">Since $Since{$symbol}</para>";
1413 if (exists $StabilityLevel{$symbol}) {
1414 my $stability = $StabilityLevel{$symbol};
1415 $AnnotationsUsed{$stability} = 1;
1416 $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1421 #############################################################################
1422 # Function : Output{Symbol,Section}ExtraLinks
1423 # Description : Returns extralinks for the symbol (if enabled).
1424 # Arguments : $symbol - the name of the function/macro begin described.
1425 #############################################################################
1429 return undef unless defined $text;
1431 # Build a char to hex map
1434 $escapes{chr($_)} = sprintf("%%%02X", $_);
1437 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1438 $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1443 sub OutputSymbolExtraLinks {
1447 if (0) { # NEW FEATURE: needs configurability
1448 my $sstr = &uri_escape($symbol);
1449 my $mstr = &uri_escape($MODULE);
1451 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1452 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$sstr">edit documentation</ulink>
1458 sub OutputSectionExtraLinks {
1459 my ($symbol,$docsymbol) = @_;
1462 if (0) { # NEW FEATURE: needs configurability
1463 my $sstr = &uri_escape($symbol);
1464 my $mstr = &uri_escape($MODULE);
1465 my $dsstr = &uri_escape($docsymbol);
1467 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1468 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$dsstr">edit documentation</ulink>
1475 #############################################################################
1476 # Function : OutputMacro
1477 # Description : Returns the synopsis and detailed description of a macro.
1478 # Arguments : $symbol - the macro.
1479 # $declaration - the declaration of the macro.
1480 #############################################################################
1483 my ($symbol, $declaration) = @_;
1484 my $id = &CreateValidSGMLID ($symbol);
1485 my $condition = &MakeConditionDescription ($symbol);
1486 my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1489 my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1490 my $title = $symbol . (@fields ? "()" : "");
1492 $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1493 $desc .= MakeIndexterms($symbol, $id);
1495 $desc .= OutputSymbolExtraLinks($symbol);
1498 $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1500 $synop .= "</entry></row>\n";
1502 # Don't output the macro definition if is is a conditional macro or it
1503 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1504 # longer than 2 lines, otherwise we get lots of complicated macros like
1506 if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1507 && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1508 my $decl_out = &CreateValidSGML ($declaration);
1509 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1511 $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1512 if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1514 my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1515 # Align each line so that if should all line up OK.
1516 $args =~ s/\n/\n$pad/gm;
1517 $desc .= &CreateValidSGML ($args);
1519 $desc .= "</programlisting>\n";
1522 $desc .= &MakeDeprecationNote($symbol);
1524 my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1525 my $parameters_output = 0;
1527 if (defined ($SymbolDocs{$symbol})) {
1528 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1530 # Try to insert the parameter table at the author's desired position.
1531 # Otherwise we need to tag it onto the end.
1532 if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
1533 $parameters_output = 1;
1535 $desc .= $symbol_docs;
1538 if ($parameters_output == 0) {
1539 $desc .= $parameters;
1542 $desc .= OutputSymbolTraits ($symbol);
1543 $desc .= "</refsect2>\n";
1544 return ($synop, $desc);
1548 #############################################################################
1549 # Function : OutputTypedef
1550 # Description : Returns the synopsis and detailed description of a typedef.
1551 # Arguments : $symbol - the typedef.
1552 # $declaration - the declaration of the typedef,
1553 # e.g. 'typedef unsigned int guint;'
1554 #############################################################################
1557 my ($symbol, $declaration) = @_;
1558 my $id = &CreateValidSGMLID ($symbol);
1559 my $condition = &MakeConditionDescription ($symbol);
1560 my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1561 my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1563 $desc .= MakeIndexterms($symbol, $id);
1565 $desc .= OutputSymbolExtraLinks($symbol);
1567 if (!defined ($DeclarationConditional{$symbol})) {
1568 my $decl_out = &CreateValidSGML ($declaration);
1569 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1572 $desc .= &MakeDeprecationNote($symbol);
1574 if (defined ($SymbolDocs{$symbol})) {
1575 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1577 $desc .= OutputSymbolTraits ($symbol);
1578 $desc .= "</refsect2>\n";
1579 return ($synop, $desc);
1583 #############################################################################
1584 # Function : OutputStruct
1585 # Description : Returns the synopsis and detailed description of a struct.
1586 # We check if it is a object struct, and if so we only output
1587 # parts of it that are noted as public fields.
1588 # We also use a different SGML ID for object structs, since the
1589 # original ID is used for the entire RefEntry.
1590 # Arguments : $symbol - the struct.
1591 # $declaration - the declaration of the struct.
1592 #############################################################################
1595 my ($symbol, $declaration) = @_;
1598 my $default_to_public = 1;
1599 if (&CheckIsObject ($symbol)) {
1600 #print "Found struct gtype: $symbol\n";
1602 $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1608 $id = &CreateValidSGMLID ($symbol . "_struct");
1609 $condition = &MakeConditionDescription ($symbol . "_struct");
1611 $id = &CreateValidSGMLID ($symbol);
1612 $condition = &MakeConditionDescription ($symbol);
1615 # Determine if it is a simple struct or it also has a typedef.
1616 my $has_typedef = 0;
1617 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1624 # For structs with typedefs we just output the struct name.
1626 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1628 $type_output = "struct";
1629 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1631 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1633 $desc .= MakeIndexterms($symbol, $id);
1635 $desc .= OutputSymbolExtraLinks($symbol);
1637 # Form a pretty-printed, private-data-removed form of the declaration
1640 if ($declaration =~ m/^\s*$/) {
1641 #print "Found opaque struct: $symbol\n";
1642 $decl_out = "typedef struct _$symbol $symbol;";
1643 } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1644 #print "Found opaque struct: $symbol\n";
1645 $decl_out = "struct $symbol;";
1647 my $public = $default_to_public;
1648 my $new_declaration = "";
1650 my $decl = $declaration;
1652 if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1653 my $struct_contents = $2;
1655 foreach $decl_line (split (/\n/, $struct_contents)) {
1656 #print "Struct line: $decl_line\n";
1657 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1659 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1662 $new_declaration .= $decl_line . "\n";
1666 if ($new_declaration) {
1667 # Strip any blank lines off the ends.
1668 $new_declaration =~ s/^\s*\n//;
1669 $new_declaration =~ s/\n\s*$/\n/;
1672 $decl_out = "typedef struct {\n" . $new_declaration
1675 $decl_out = "struct $symbol {\n" . $new_declaration
1680 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1681 "Couldn't parse struct:\n$declaration");
1684 # If we couldn't parse the struct or it was all private, output an
1685 # empty struct declaration.
1686 if ($decl_out eq "") {
1688 $decl_out = "typedef struct _$symbol $symbol;";
1690 $decl_out = "struct $symbol;";
1695 $decl_out = &CreateValidSGML ($decl_out);
1696 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1698 $desc .= &MakeDeprecationNote($symbol);
1700 if (defined ($SymbolDocs{$symbol})) {
1701 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1704 # Create a table of fields and descriptions
1706 # FIXME: Inserting  's into the produced type declarations here would
1707 # improve the output in most situations ... except for function
1708 # members of structs!
1709 my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1712 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1714 my $params = $SymbolParams{$symbol};
1716 # If no parameters are filled in, we don't generate the description
1717 # table, for backwards compatibility.
1720 if (defined $params) {
1721 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1722 if ($params->[$i] =~ /\S/) {
1730 my %field_descrs = @$params;
1731 my $missing_parameters = "";
1732 my $unused_parameters = "";
1735 <refsect3 role="struct_members">\n<title>Members</title>
1736 <informaltable role="struct_members_table" pgwide="1" frame="none">
1738 <colspec colname="struct_members_name" colwidth="300px"/>
1739 <colspec colname="struct_members_description"/>
1740 <colspec colname="struct_members_annotations" colwidth="200px"/>
1745 my $field_name = shift @fields;
1746 my $text = shift @fields;
1747 my $field_descr = $field_descrs{$field_name};
1748 my $param_annotations = "";
1750 $desc .= "<row><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1751 if (defined $field_descr) {
1752 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1753 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1755 $field_descr =~ s/^(\s|\n)+//msg;
1756 $field_descr =~ s/(\s|\n)+$//msg;
1757 $desc .= "<listitem>$field_descr</listitem>\n";
1758 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1759 delete $field_descrs{$field_name};
1761 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1762 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1763 if ($missing_parameters ne "") {
1764 $missing_parameters .= ", ".$field_name;
1766 $missing_parameters = $field_name;
1768 $desc .= "<entry /><entry />\n";
1770 $desc .= "</row>\n";
1772 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1773 foreach my $field_name (keys %field_descrs) {
1774 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1775 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1776 if ($unused_parameters ne "") {
1777 $unused_parameters .= ", ".$field_name;
1779 $unused_parameters = $field_name;
1783 # remember missing/unused parameters (needed in tmpl-free build)
1784 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1785 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1787 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1788 $AllUnusedSymbols{$symbol}=$unused_parameters;
1792 if (scalar(@fields) > 0) {
1793 if (! exists ($AllIncompleteSymbols{$symbol})) {
1794 $AllIncompleteSymbols{$symbol}="<items>";
1795 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1796 "Field descriptions for struct $symbol are missing in source code comment block.");
1797 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1802 $desc .= OutputSymbolTraits ($symbol);
1803 $desc .= "</refsect2>\n";
1804 return ($synop, $desc);
1808 #############################################################################
1809 # Function : OutputUnion
1810 # Description : Returns the synopsis and detailed description of a union.
1811 # Arguments : $symbol - the union.
1812 # $declaration - the declaration of the union.
1813 #############################################################################
1816 my ($symbol, $declaration) = @_;
1819 if (&CheckIsObject ($symbol)) {
1820 @TRACE@("Found union gtype: $symbol\n");
1827 $id = &CreateValidSGMLID ($symbol . "_union");
1828 $condition = &MakeConditionDescription ($symbol . "_union");
1830 $id = &CreateValidSGMLID ($symbol);
1831 $condition = &MakeConditionDescription ($symbol);
1834 # Determine if it is a simple struct or it also has a typedef.
1835 my $has_typedef = 0;
1836 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1843 # For unions with typedefs we just output the union name.
1845 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1847 $type_output = "union";
1848 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1850 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1852 $desc .= MakeIndexterms($symbol, $id);
1854 $desc .= OutputSymbolExtraLinks($symbol);
1855 $desc .= &MakeDeprecationNote($symbol);
1857 if (defined ($SymbolDocs{$symbol})) {
1858 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1861 # Create a table of fields and descriptions
1863 # FIXME: Inserting  's into the produced type declarations here would
1864 # improve the output in most situations ... except for function
1865 # members of structs!
1866 my @fields = ParseStructDeclaration($declaration, 0,
1869 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1871 my $params = $SymbolParams{$symbol};
1873 # If no parameters are filled in, we don't generate the description
1874 # table, for backwards compatibility
1877 if (defined $params) {
1878 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1879 if ($params->[$i] =~ /\S/) {
1887 my %field_descrs = @$params;
1888 my $missing_parameters = "";
1889 my $unused_parameters = "";
1892 <refsect3 role="union_members">\n<title>Members</title>
1893 <informaltable role="union_members_table" pgwide="1" frame="none">
1895 <colspec colname="union_members_name" colwidth="300px"/>
1896 <colspec colname="union_members_description"/>
1897 <colspec colname="union_members_annotations" colwidth="200px"/>
1902 my $field_name = shift @fields;
1903 my $text = shift @fields;
1904 my $field_descr = $field_descrs{$field_name};
1905 my $param_annotations = "";
1907 $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1908 if (defined $field_descr) {
1909 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1910 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1913 $field_descr =~ s/^(\s|\n)+//msg;
1914 $field_descr =~ s/(\s|\n)+$//msg;
1915 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1916 delete $field_descrs{$field_name};
1918 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1919 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1920 if ($missing_parameters ne "") {
1921 $missing_parameters .= ", ".$field_name;
1923 $missing_parameters = $field_name;
1925 $desc .= "<entry /><entry />\n";
1927 $desc .= "</row>\n";
1929 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1930 foreach my $field_name (keys %field_descrs) {
1931 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1932 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1933 if ($unused_parameters ne "") {
1934 $unused_parameters .= ", ".$field_name;
1936 $unused_parameters = $field_name;
1940 # remember missing/unused parameters (needed in tmpl-free build)
1941 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1942 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1944 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1945 $AllUnusedSymbols{$symbol}=$unused_parameters;
1949 if (scalar(@fields) > 0) {
1950 if (! exists ($AllIncompleteSymbols{$symbol})) {
1951 $AllIncompleteSymbols{$symbol}="<items>";
1952 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1953 "Field descriptions for union $symbol are missing in source code comment block.");
1954 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1959 $desc .= OutputSymbolTraits ($symbol);
1960 $desc .= "</refsect2>\n";
1961 return ($synop, $desc);
1965 #############################################################################
1966 # Function : OutputEnum
1967 # Description : Returns the synopsis and detailed description of a enum.
1968 # Arguments : $symbol - the enum.
1969 # $declaration - the declaration of the enum.
1970 #############################################################################
1973 my ($symbol, $declaration) = @_;
1976 if (&CheckIsObject ($symbol)) {
1977 #print "Found enum gtype: $symbol\n";
1984 $id = &CreateValidSGMLID ($symbol . "_enum");
1985 $condition = &MakeConditionDescription ($symbol . "_enum");
1987 $id = &CreateValidSGMLID ($symbol);
1988 $condition = &MakeConditionDescription ($symbol);
1991 my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1992 my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1994 $desc .= MakeIndexterms($symbol, $id);
1996 $desc .= OutputSymbolExtraLinks($symbol);
1997 $desc .= &MakeDeprecationNote($symbol);
1999 if (defined ($SymbolDocs{$symbol})) {
2000 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2003 # Create a table of fields and descriptions
2005 my @fields = ParseEnumDeclaration($declaration);
2006 my $params = $SymbolParams{$symbol};
2008 # If nothing at all is documented log a single summary warning at the end.
2009 # Otherwise, warn about each undocumented item.
2012 if (defined $params) {
2013 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
2014 if ($params->[$i] =~ /\S/) {
2021 my %field_descrs = (defined $params ? @$params : ());
2022 my $missing_parameters = "";
2023 my $unused_parameters = "";
2026 <refsect3 role="enum_members">\n<title>Members</title>
2027 <informaltable role="enum_members_table" pgwide="1" frame="none">
2029 <colspec colname="enum_members_name" colwidth="300px"/>
2030 <colspec colname="enum_members_description"/>
2031 <colspec colname="enum_members_annotations" colwidth="200px"/>
2035 for my $field_name (@fields) {
2036 my $field_descr = $field_descrs{$field_name};
2037 my $param_annotations = "";
2039 $id = &CreateValidSGMLID ($field_name);
2040 $condition = &MakeConditionDescription ($field_name);
2041 $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2042 if (defined $field_descr) {
2043 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2044 $field_descr = &ConvertMarkDown($symbol, $field_descr);
2045 $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2046 delete $field_descrs{$field_name};
2049 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2050 "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2051 if ($missing_parameters ne "") {
2052 $missing_parameters .= ", ".$field_name;
2054 $missing_parameters = $field_name;
2057 $desc .= "<entry /><entry />\n";
2059 $desc .= "</row>\n";
2061 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2062 foreach my $field_name (keys %field_descrs) {
2063 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2064 "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2065 if ($unused_parameters ne "") {
2066 $unused_parameters .= ", ".$field_name;
2068 $unused_parameters = $field_name;
2072 # remember missing/unused parameters (needed in tmpl-free build)
2073 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2074 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2076 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2077 $AllUnusedSymbols{$symbol}=$unused_parameters;
2081 if (scalar(@fields) > 0) {
2082 if (! exists ($AllIncompleteSymbols{$symbol})) {
2083 $AllIncompleteSymbols{$symbol}="<items>";
2084 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2085 "Value descriptions for $symbol are missing in source code comment block.");
2090 $desc .= OutputSymbolTraits ($symbol);
2091 $desc .= "</refsect2>\n";
2092 return ($synop, $desc);
2096 #############################################################################
2097 # Function : OutputVariable
2098 # Description : Returns the synopsis and detailed description of a variable.
2099 # Arguments : $symbol - the extern'ed variable.
2100 # $declaration - the declaration of the variable.
2101 #############################################################################
2103 sub OutputVariable {
2104 my ($symbol, $declaration) = @_;
2105 my $id = &CreateValidSGMLID ($symbol);
2106 my $condition = &MakeConditionDescription ($symbol);
2108 @TRACE@("ouputing variable: '$symbol' '$declaration'");
2111 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*;/) {
2112 my $mod1 = defined ($1) ? $1 : "";
2113 my $ptr = defined ($3) ? $3 : "";
2114 my $space = defined ($4) ? $4 : "";
2115 my $mod2 = defined ($5) ? $5 : "";
2116 $type_output = "extern $mod1$ptr$space$mod2";
2117 } 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*=/) {
2118 my $mod1 = defined ($1) ? $1 : "";
2119 my $ptr = defined ($3) ? $3 : "";
2120 my $space = defined ($4) ? $4 : "";
2121 my $mod2 = defined ($5) ? $5 : "";
2122 $type_output = "$mod1$ptr$space$mod2";
2124 $type_output = "extern";
2126 my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2128 my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2130 $desc .= MakeIndexterms($symbol, $id);
2132 $desc .= OutputSymbolExtraLinks($symbol);
2134 my $decl_out = &CreateValidSGML ($declaration);
2135 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2137 $desc .= &MakeDeprecationNote($symbol);
2139 if (defined ($SymbolDocs{$symbol})) {
2140 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2142 $desc .= OutputSymbolTraits ($symbol);
2143 $desc .= "</refsect2>\n";
2144 return ($synop, $desc);
2148 #############################################################################
2149 # Function : OutputFunction
2150 # Description : Returns the synopsis and detailed description of a function.
2151 # Arguments : $symbol - the function.
2152 # $declaration - the declaration of the function.
2153 #############################################################################
2155 sub OutputFunction {
2156 my ($symbol, $declaration, $symbol_type) = @_;
2157 my $id = &CreateValidSGMLID ($symbol);
2158 my $condition = &MakeConditionDescription ($symbol);
2160 # Take out the return type $1 $2 $3
2161 $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//;
2162 my $type_modifier = defined($1) ? $1 : "";
2165 # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2166 $pointer =~ s/\s+$//;
2167 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2169 #if ($symbol_type eq 'USER_FUNCTION') {
2170 # $start = "typedef ";
2173 # We output const rather than G_CONST_RETURN.
2174 $type_modifier =~ s/G_CONST_RETURN/const/g;
2175 $pointer =~ s/G_CONST_RETURN/const/g;
2176 $pointer =~ s/^\s+/ /g;
2178 my $ret_type_output;
2179 $ret_type_output = "$start$type_modifier$xref$pointer\n";
2182 $indent_len = length ($symbol) + 2;
2183 my $char1 = my $char2 = my $char3 = "";
2184 if ($symbol_type eq 'USER_FUNCTION') {
2186 $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2188 $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2191 my ($symbol_output, $symbol_desc_output);
2192 $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2193 if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2194 $symbol_desc_output = "$char1$char2$symbol$char3 ";
2196 $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2197 $symbol_desc_output = "$char1$char2$symbol$char3\n"
2198 . (' ' x ($indent_len - 1));
2201 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";
2203 my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol} ()</title>\n";
2205 $desc .= MakeIndexterms($symbol, $id);
2207 $desc .= OutputSymbolExtraLinks($symbol);
2209 $desc .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2211 my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2213 &tagify($_[0],"parameter");
2216 for (my $i = 1; $i <= $#fields; $i += 2) {
2217 my $field_name = $fields[$i];
2220 $desc .= "$field_name";
2223 . (' ' x $indent_len)
2229 $desc .= ");</programlisting>\n";
2231 $desc .= &MakeDeprecationNote($symbol);
2233 my $parameters = &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2234 my $parameters_output = 0;
2236 if (defined ($SymbolDocs{$symbol})) {
2237 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2239 # Try to insert the parameter table at the author's desired position.
2240 # Otherwise we need to tag it onto the end.
2241 # FIXME: document that in the user manual and make it useable for other
2243 if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
2244 $parameters_output = 1;
2246 $desc .= $symbol_docs;
2249 if ($parameters_output == 0) {
2250 $desc .= $parameters;
2253 $desc .= OutputSymbolTraits ($symbol);
2254 $desc .= "</refsect2>\n";
2255 return ($synop, $desc);
2259 #############################################################################
2260 # Function : OutputParamDescriptions
2261 # Description : Returns the DocBook output describing the parameters of a
2262 # function, macro or signal handler.
2263 # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2264 # handlers have an implicit user_data parameter last.
2265 # $symbol - the name of the function/macro being described.
2266 # @fields - parsed fields from the declaration, used to determine
2267 # undocumented/unused entries
2268 #############################################################################
2270 sub OutputParamDescriptions {
2271 my ($symbol_type, $symbol, @fields) = @_;
2273 my $params = $SymbolParams{$symbol};
2275 my %field_descrs = ();
2278 %field_descrs = @fields;
2279 delete $field_descrs{"void"};
2280 delete $field_descrs{"Returns"};
2283 if (defined $params) {
2285 my $params_desc = "";
2286 my $missing_parameters = "";
2287 my $unused_parameters = "";
2290 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2291 my $param_name = $$params[$j];
2292 my $param_desc = $$params[$j + 1];
2293 my $param_annotations = "";
2295 ($param_desc,$param_annotations) = & ExpandAnnotation($symbol, $param_desc);
2296 $param_desc = &ConvertMarkDown($symbol, $param_desc);
2298 $param_desc =~ s/^(\s|\n)+//msg;
2299 $param_desc =~ s/(\s|\n)+$//msg;
2300 if ($param_name eq "Returns") {
2301 $returns = "$param_desc\n<para>$param_annotations</para>";
2302 } elsif ($param_name eq "void") {
2303 #print "!!!! void in params for $symbol?\n";
2306 if (!defined $field_descrs{$param_name}) {
2307 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2308 "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2309 if ($unused_parameters ne "") {
2310 $unused_parameters .= ", ".$param_name;
2312 $unused_parameters = $param_name;
2315 delete $field_descrs{$param_name};
2318 if($param_desc ne "") {
2319 $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";
2324 foreach my $param_name (keys %field_descrs) {
2325 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2326 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2327 if ($missing_parameters ne "") {
2328 $missing_parameters .= ", ".$param_name;
2330 $missing_parameters = $param_name;
2334 # Signals have an implicit user_data parameter which we describe.
2335 if ($symbol_type eq "SIGNAL") {
2336 $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";
2339 # Start a table if we need one.
2340 if ($params_desc ne "") {
2342 <refsect3 role="parameters">\n<title>Parameters</title>
2343 <informaltable role="parameters_table" pgwide="1" frame="none">
2345 <colspec colname="parameters_name" colwidth="150px"/>
2346 <colspec colname="parameters_description"/>
2347 <colspec colname="parameters_annotations" colwidth="200px"/>
2350 $output .= $params_desc;
2351 $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2354 # Output the returns info last
2355 if ($returns ne "") {
2357 <refsect3 role=\"returns\">\n<title>Returns</title>
2359 $output .= $returns;
2360 $output .= "\n</refsect3>";
2363 # remember missing/unused parameters (needed in tmpl-free build)
2364 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2365 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2367 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2368 $AllUnusedSymbols{$symbol}=$unused_parameters;
2371 if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2372 if (! exists ($AllIncompleteSymbols{$symbol})) {
2373 $AllIncompleteSymbols{$symbol}="<parameters>";
2381 #############################################################################
2382 # Function : ParseStabilityLevel
2383 # Description : Parses a stability level and outputs a warning if it isn't
2385 # Arguments : $stability - the stability text.
2386 # $file, $line - context for error message
2387 # $message - description of where the level is from, to use in
2388 # any error message.
2389 # Returns : The parsed stability level string.
2390 #############################################################################
2392 sub ParseStabilityLevel {
2393 my ($stability, $file, $line, $message) = @_;
2395 $stability =~ s/^\s*//;
2396 $stability =~ s/\s*$//;
2397 if ($stability =~ m/^stable$/i) {
2398 $stability = "Stable";
2399 } elsif ($stability =~ m/^unstable$/i) {
2400 $stability = "Unstable";
2401 } elsif ($stability =~ m/^private$/i) {
2402 $stability = "Private";
2404 &LogWarning ($file, $line, "$message is $stability.".
2405 "It should be one of these: Stable, Unstable, or Private.");
2411 #############################################################################
2412 # Function : OutputSGMLFile
2413 # Description : Outputs the final DocBook file for one section.
2414 # Arguments : $file - the name of the file.
2415 # $title - the title from the $MODULE-sections.txt file, which
2416 # will be overridden by the title in the template file.
2417 # $section_id - the SGML id to use for the toplevel tag.
2418 # $includes - comma-separates list of include files added at top of
2419 # synopsis, with '<' '>' around them (if not already enclosed in "").
2420 # $functions_synop - reference to the DocBook for the Functions Synopsis part.
2421 # $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2422 # $functions_details - reference to the DocBook for the Functions Details part.
2423 # $other_details - reference to the DocBook for the Types and Values Details part.
2424 # $signal_synop - reference to the DocBook for the Signal Synopsis part
2425 # $signal_desc - reference to the DocBook for the Signal Description part
2426 # $args_synop - reference to the DocBook for the Arg Synopsis part
2427 # $args_desc - reference to the DocBook for the Arg Description part
2428 # $hierarchy - reference to the DocBook for the Object Hierarchy part
2429 # $interfaces - reference to the DocBook for the Interfaces part
2430 # $implementations - reference to the DocBook for the Known Implementations part
2431 # $prerequisites - reference to the DocBook for the Prerequisites part
2432 # $derived - reference to the DocBook for the Derived Interfaces part
2433 # $file_objects - reference to an array of objects in this file
2434 #############################################################################
2436 sub OutputSGMLFile {
2437 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) = @_;
2439 #print "Output sgml for file $file with title '$title'\n";
2441 # The edited title overrides the one from the sections file.
2442 my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2443 if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2444 $title = $new_title;
2445 #print "Found title: $title\n";
2447 my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2448 if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2451 # Don't use ConvertMarkDown here for now since we don't want blocks
2452 $short_desc = &ExpandAbbreviations("$title:Short_description",
2454 #print "Found short_desc: $short_desc";
2456 my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2457 if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2460 $long_desc = &ConvertMarkDown("$title:Long_description",
2462 #print "Found long_desc: $long_desc";
2464 my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2465 if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2468 $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2469 #print "Found see_also: $see_also";
2472 $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2474 my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2475 if (!defined ($stability) || $stability =~ m/^\s*$/) {
2478 $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2479 #print "Found stability: $stability";
2482 $AnnotationsUsed{$stability} = 1;
2483 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2484 } elsif ($DEFAULT_STABILITY) {
2485 $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2486 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2489 my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2490 if (!defined ($image) || $image =~ m/^\s*$/) {
2498 if ($image =~ /jpe?g$/i) {
2499 $format = "format='JPEG'";
2500 } elsif ($image =~ /png$/i) {
2501 $format = "format='PNG'";
2502 } elsif ($image =~ /svg$/i) {
2503 $format = "format='SVG'";
2508 $image = " <inlinegraphic fileref='$image' $format/>\n"
2511 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2513 my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2516 my $include_output = "";
2518 $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2520 foreach $include (split (/,/, $includes)) {
2521 if ($include =~ m/^\".+\"$/) {
2522 $include_output .= "#include ${include}\n";
2525 $include =~ s/^\s+|\s+$//gs;
2526 $include_output .= "#include <${include}>\n";
2529 $include_output .= "</synopsis></refsect1>\n";
2532 my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2534 my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT";
2535 my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new";
2537 open (OUTPUT, ">$new_sgml_file")
2538 || die "Can't create $new_sgml_file: $!";
2540 my $object_anchors = "";
2541 foreach my $object (@$file_objects) {
2542 next if ($object eq $section_id);
2543 my $id = CreateValidSGMLID($object);
2544 #print "Debug: Adding anchor for $object\n";
2545 $object_anchors .= "<anchor id=\"$id\"$empty_element_end";
2548 # We used to output this, but is messes up our UpdateFileIfChanged code
2549 # since it changes every day (and it is only used in the man pages):
2550 # "<refentry id="$section_id" revision="$mday $month $year">"
2552 if ($OUTPUT_FORMAT eq "xml") {
2553 print OUTPUT $doctype_header;
2557 <refentry id="$section_id">
2559 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2560 <manvolnum>3</manvolnum>
2563 $image</refmiscinfo>
2566 <refname>$title</refname>
2567 <refpurpose>$short_desc</refpurpose>
2570 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2572 <refsect1 id="$section_id.description" role="desc">
2573 <title role="desc.title">Description</title>
2574 $extralinks$long_desc
2576 <refsect1 id="$section_id.functions_details" role="details">
2577 <title role="details.title">Functions</title>
2580 <refsect1 id="$section_id.other_details" role="details">
2581 <title role="details.title">Types and Values</title>
2584 $$args_desc$$signals_desc$see_also
2589 return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2593 #############################################################################
2594 # Function : OutputExtraFile
2595 # Description : Copies an "extra" DocBook file into the output directory,
2596 # expanding abbreviations
2597 # Arguments : $file - the source file.
2598 #############################################################################
2599 sub OutputExtraFile {
2604 ($basename = $file) =~ s!^.*/!!;
2606 my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename";
2607 my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new";
2611 open(EXTRA_FILE, "<$file") || die "Can't open $file";
2615 $contents = <EXTRA_FILE>;
2618 open (OUTPUT, ">$new_sgml_file")
2619 || die "Can't create $new_sgml_file: $!";
2621 print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2624 return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2626 #############################################################################
2627 # Function : OutputBook
2628 # Description : Outputs the SGML entities that need to be included into the
2629 # main SGML file for the module.
2630 # Arguments : $book_top - the declarations of the entities, which are added
2631 # at the top of the main SGML file.
2632 # $book_bottom - the references to the entities, which are
2633 # added in the main SGML file at the desired position.
2634 #############################################################################
2637 my ($book_top, $book_bottom) = @_;
2639 my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top";
2640 my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new";
2642 open (OUTPUT, ">$new_file")
2643 || die "Can't create $new_file: $!";
2644 print OUTPUT $book_top;
2647 &UpdateFileIfChanged ($old_file, $new_file, 0);
2650 $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom";
2651 $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new";
2653 open (OUTPUT, ">$new_file")
2654 || die "Can't create $new_file: $!";
2655 print OUTPUT $book_bottom;
2658 &UpdateFileIfChanged ($old_file, $new_file, 0);
2661 # If the main SGML/XML file hasn't been created yet, we create it here.
2662 # The user can tweak it later.
2663 if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2664 open (OUTPUT, ">$MAIN_SGML_FILE")
2665 || die "Can't create $MAIN_SGML_FILE: $!";
2667 if ($OUTPUT_FORMAT eq "xml") {
2669 <?xml version="1.0"?>
2670 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2671 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2673 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
2679 <!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
2688 <title>$MODULE Reference Manual</title>
2690 for $MODULE [VERSION].
2691 The latest version of this documentation can be found on-line at
2692 <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2697 <title>[Insert title here]</title>
2701 if (-e $OBJECT_TREE_FILE) {
2703 <chapter id="object-tree">
2704 <title>Object Hierarchy</title>
2705 <xi:include href="xml/tree_index.sgml"/>
2711 <index id="api-index-full">
2712 <title>API Index</title>
2713 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2715 <index id="deprecated-api-index" role="deprecated">
2716 <title>Index of deprecated API</title>
2717 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2720 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2729 #############################################################################
2730 # Function : CreateValidSGML
2731 # Description : This turns any chars which are used in SGML into entities,
2732 # e.g. '<' into '<'
2733 # Arguments : $text - the text to turn into proper SGML.
2734 #############################################################################
2736 sub CreateValidSGML {
2738 $text =~ s/&/&/g; # Do this first, or the others get messed up.
2739 $text =~ s/</</g;
2740 $text =~ s/>/>/g;
2741 # browers render single tabs inconsistently
2742 $text =~ s/([^\s])\t([^\s])/$1 $2/g;
2746 #############################################################################
2747 # Function : ConvertSGMLChars
2748 # Description : This is used for text in source code comment blocks, to turn
2749 # chars which are used in SGML into entities, e.g. '<' into
2750 # '<'. Depending on $INLINE_MARKUP_MODE, this is done
2751 # unconditionally or only if the character doesn't seem to be
2752 # part of an SGML construct (tag or entity reference).
2753 # Arguments : $text - the text to turn into proper SGML.
2754 #############################################################################
2756 sub ConvertSGMLChars {
2757 my ($symbol, $text) = @_;
2759 if ($INLINE_MARKUP_MODE) {
2760 # For the XML/SGML mode only convert to entities outside CDATA sections.
2761 return &ModifyXMLElements ($text, $symbol,
2762 "<!\\[CDATA\\[|<programlisting[^>]*>",
2763 \&ConvertSGMLCharsEndTag,
2764 \&ConvertSGMLCharsCallback);
2766 # For the simple non-sgml mode, convert to entities everywhere.
2767 $text =~ s/&/&/g; # Do this first, or the others get messed up.
2768 $text =~ s/</</g;
2769 $text =~ s/>/>/g;
2775 sub ConvertSGMLCharsEndTag {
2776 if ($_[0] eq "<!\[CDATA\[") {
2779 return "</programlisting>";
2783 sub ConvertSGMLCharsCallback {
2784 my ($text, $symbol, $tag) = @_;
2786 if ($tag =~ m/^<programlisting/) {
2787 # We can handle <programlisting> specially here.
2788 return &ModifyXMLElements ($text, $symbol,
2790 \&ConvertSGMLCharsEndTag,
2791 \&ConvertSGMLCharsCallback2);
2792 } elsif ($tag eq "") {
2793 # If we're not in CDATA convert to entities.
2794 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2795 $text =~ s/<(?![a-zA-Z\/!])/</g;
2796 # Allow ">" at beginning of string for blockquote markdown
2797 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2799 # Handle "#include <xxxxx>"
2800 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2806 sub ConvertSGMLCharsCallback2 {
2807 my ($text, $symbol, $tag) = @_;
2809 # If we're not in CDATA convert to entities.
2810 # We could handle <programlisting> differently, though I'm not sure it helps.
2812 # replace only if its not a tag
2813 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2814 $text =~ s/<(?![a-zA-Z\/!])/</g;
2815 $text =~ s/(?<![a-zA-Z0-9"'\/-])>/>/g;
2817 # Handle "#include <xxxxx>"
2818 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2824 #############################################################################
2825 # Function : ExpandAnnotation
2826 # Description : This turns annotations into acronym tags.
2827 # Arguments : $symbol - the symbol being documented, for error messages.
2828 # $text - the text to expand.
2829 #############################################################################
2830 sub ExpandAnnotation {
2831 my ($symbol, $param_desc) = @_;
2832 my $param_annotations = "";
2834 # look for annotations at the start of the comment part
2835 if ($param_desc =~ m%^\s*\((.*?)\):%) {
2840 @annotations = split(/\)\s*\(/,$1);
2841 foreach $annotation (@annotations) {
2842 # need to search for the longest key-match in %AnnotationDefinition
2844 my $match_annotation="";
2846 foreach $annotationdef (keys %AnnotationDefinition) {
2847 if ($annotation =~ m/^$annotationdef/) {
2848 if (length($annotationdef)>$match_length) {
2849 $match_length=length($annotationdef);
2850 $match_annotation=$annotationdef;
2854 my $annotation_extra = "";
2855 if ($match_annotation ne "") {
2856 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2857 $annotation_extra = " $1";
2859 $AnnotationsUsed{$match_annotation} = 1;
2860 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2863 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2864 "unknown annotation \"$annotation\" in documentation for $symbol.");
2865 $param_annotations .= "[$annotation]";
2869 $param_desc =~ m/^(.*?)\.*\s*$/s;
2870 $param_desc = "$1. ";
2872 if ($param_annotations ne "") {
2873 $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2875 return ($param_desc, $param_annotations);
2878 #############################################################################
2879 # Function : ExpandAbbreviations
2880 # Description : This turns the abbreviations function(), macro(), @param,
2881 # %constant, and #symbol into appropriate DocBook markup.
2882 # CDATA sections and <programlisting> parts are skipped.
2883 # Arguments : $symbol - the symbol being documented, for error messages.
2884 # $text - the text to expand.
2885 #############################################################################
2887 sub ExpandAbbreviations {
2888 my ($symbol, $text) = @_;
2890 # Note: This is a fallback and normally done in the markdown parser
2892 # Convert "|[" and "]|" into the start and end of program listing examples.
2893 # Support \[<!-- language="C" --> modifiers
2894 $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2895 $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2896 $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2898 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2900 return &ModifyXMLElements ($text, $symbol,
2901 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2902 \&ExpandAbbreviationsEndTag,
2903 \&ExpandAbbreviationsCallback);
2907 # Returns the end tag (as a regexp) corresponding to the given start tag.
2908 sub ExpandAbbreviationsEndTag {
2909 my ($start_tag) = @_;
2911 if ($start_tag eq "<!\[CDATA\[") {
2913 } elsif ($start_tag eq "<!DOCTYPE") {
2915 } elsif ($start_tag =~ m/<(\w+)/) {
2920 # Called inside or outside each CDATA or <programlisting> section.
2921 sub ExpandAbbreviationsCallback {
2922 my ($text, $symbol, $tag) = @_;
2924 if ($tag =~ m/^<programlisting/) {
2925 # Handle any embedded CDATA sections.
2926 return &ModifyXMLElements ($text, $symbol,
2928 \&ExpandAbbreviationsEndTag,
2929 \&ExpandAbbreviationsCallback2);
2930 } elsif ($tag eq "") {
2931 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2933 # We are outside any CDATA or <programlisting> sections, so we expand
2934 # any gtk-doc abbreviations.
2936 # Convert '@param()'
2937 # FIXME: we could make those also links ($symbol.$2), but that would be less
2938 # useful as the link target is a few lines up or down
2939 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2941 # Convert 'function()' or 'macro()'.
2942 # if there is abc_*_def() we don't want to make a link to _def()
2943 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2944 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2945 # handle #Object.func()
2946 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2948 # Convert '@param', but not '\@param'.
2949 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2950 $text =~ s/\\\@/\@/g;
2952 # Convert '%constant', but not '\%constant'.
2953 # Also allow negative numbers, e.g. %-1.
2954 $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2955 $text =~ s/\\\%/\%/g;
2957 # Convert '#symbol', but not '\#symbol'.
2958 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2965 # This is called inside a <programlisting>
2966 sub ExpandAbbreviationsCallback2 {
2967 my ($text, $symbol, $tag) = @_;
2970 # We are inside a <programlisting> but outside any CDATA sections,
2971 # so we expand any gtk-doc abbreviations.
2972 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2973 # why not just call it
2974 $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2975 } elsif ($tag eq "<![CDATA[") {
2976 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2977 $text = &ReplaceEntities ($text, $symbol);
2984 my ($symbol, $tag) = @_;;
2987 # Check for things like '#include', '#define', and skip them.
2988 if ($PreProcessorDirectives{$symbol}) {
2992 # Get rid of special suffixes ('-struct','-enum').
2993 $text =~ s/-struct$//;
2994 $text =~ s/-enum$//;
2996 # If the symbol is in the form "Object::signal", then change the symbol to
2997 # "Object-signal" and use "signal" as the text.
2998 if ($symbol =~ s/::/-/) {
3002 # If the symbol is in the form "Object:property", then change the symbol to
3003 # "Object--property" and use "property" as the text.
3004 if ($symbol =~ s/:/--/) {
3009 $text = tagify ($text, $tag);
3012 return &MakeXRef($symbol, $text);
3016 #############################################################################
3017 # Function : ModifyXMLElements
3018 # Description : Looks for given XML element tags within the text, and calls
3019 # the callback on pieces of text inside & outside those elements.
3020 # Used for special handling of text inside things like CDATA
3021 # and <programlisting>.
3022 # Arguments : $text - the text.
3023 # $symbol - the symbol currently being documented (only used for
3025 # $start_tag_regexp - the regular expression to match start tags.
3026 # e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3027 # CDATA sections or programlisting elements.
3028 # $end_tag_func - function which is passed the matched start tag
3029 # and should return the appropriate end tag string regexp.
3030 # $callback - callback called with each part of the text. It is
3031 # called with a piece of text, the symbol being
3032 # documented, and the matched start tag or "" if the text
3033 # is outside the XML elements being matched.
3034 #############################################################################
3035 sub ModifyXMLElements {
3036 my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3037 my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3040 while ($text =~ m/$start_tag_regexp/s) {
3041 $before_tag = $`; # Prematch for last successful match string
3042 $start_tag = $&; # Last successful match
3043 $text = $'; # Postmatch for last successful match string
3045 $result .= &$callback ($before_tag, $symbol, "");
3046 $result .= $start_tag;
3048 # get the matching end-tag for current tag
3049 $end_tag_regexp = &$end_tag_func ($start_tag);
3051 if ($text =~ m/$end_tag_regexp/s) {
3056 $result .= &$callback ($before_tag, $symbol, $start_tag);
3057 $result .= $end_tag;
3059 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3060 "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3061 # Just assume it is all inside the tag.
3062 $result .= &$callback ($text, $symbol, $start_tag);
3067 # Handle any remaining text outside the tags.
3068 $result .= &$callback ($text, $symbol, "");
3077 # Adds a tag around some text.
3078 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3080 my ($text, $elem) = @_;
3081 return "<" . $elem . ">" . $text . "</" . $elem . ">";
3085 #############################################################################
3086 # Function : MakeXRef
3087 # Description : This returns a cross-reference link to the given symbol.
3088 # Though it doesn't try to do this for a few standard C types
3089 # that it knows won't be in the documentation.
3090 # Arguments : $symbol - the symbol to try to create a XRef to.
3091 # $text - text text to put inside the XRef, defaults to $symbol
3092 #############################################################################
3095 my ($symbol, $text) = ($_[0], $_[1]);
3097 $symbol =~ s/^\s+//;
3098 $symbol =~ s/\s+$//;
3100 if (!defined($text)) {
3103 # Get rid of special suffixes ('-struct','-enum').
3104 $text =~ s/-struct$//;
3105 $text =~ s/-enum$//;
3108 if ($symbol =~ m/ /) {
3112 #print "Getting type link for $symbol -> $text\n";
3114 my $symbol_id = &CreateValidSGMLID ($symbol);
3115 return "<link linkend=\"$symbol_id\">$text</link>";
3119 #############################################################################
3120 # Function : MakeIndexterms
3121 # Description : This returns a indexterm elements for the given symbol
3122 # Arguments : $symbol - the symbol to create indexterms for
3123 #############################################################################
3125 sub MakeIndexterms {
3126 my ($symbol, $id) = @_;
3130 # make the index useful, by ommiting the namespace when sorting
3131 if ($NAME_SPACE ne "") {
3132 if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3133 $sortas=" sortas=\"$1\"";
3137 if (exists $Deprecated{$symbol}) {
3138 $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3139 $IndexEntriesDeprecated{$symbol}=$id;
3140 $IndexEntriesFull{$symbol}=$id;
3142 if (exists $Since{$symbol}) {
3143 my $since = $Since{$symbol};
3147 $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3149 $IndexEntriesSince{$symbol}=$id;
3150 $IndexEntriesFull{$symbol}=$id;
3153 $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3154 $IndexEntriesFull{$symbol}=$id;
3160 #############################################################################
3161 # Function : MakeDeprecationNote
3162 # Description : This returns a deprecation warning for the given symbol.
3163 # Arguments : $symbol - the symbol to try to create a warning for.
3164 #############################################################################
3166 sub MakeDeprecationNote {
3167 my ($symbol) = $_[0];
3169 if (exists $Deprecated{$symbol}) {
3172 $desc .= "<warning><para><literal>$symbol</literal> ";
3174 $note = $Deprecated{$symbol};
3176 if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3177 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3179 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3181 $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3185 $note = &ConvertMarkDown($symbol, $note);
3186 $desc .= " " . $note;
3188 $desc .= "</warning>\n";
3193 #############################################################################
3194 # Function : MakeConditionDescription
3195 # Description : This returns a sumary of conditions for the given symbol.
3196 # Arguments : $symbol - the symbol to try to create the sumary.
3197 #############################################################################
3199 sub MakeConditionDescription {
3200 my ($symbol) = $_[0];
3203 if (exists $Deprecated{$symbol}) {
3208 if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3209 $desc .= "deprecated:$1";
3211 $desc .= "deprecated";
3215 if (exists $Since{$symbol}) {
3220 if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3221 $desc .= "since:$1";
3227 if (exists $StabilityLevel{$symbol}) {
3231 $desc .= "stability:".$StabilityLevel{$symbol};
3236 $cond =~ s/\"/"/g;
3237 $desc=" condition=\"".$cond."\"";
3238 #print "condition for '$symbol' = '$desc'\n";
3243 #############################################################################
3244 # Function : GetHierarchy
3245 # Description : Returns the DocBook output describing the ancestors and
3246 # immediate children of a GObject subclass. It uses the
3247 # global @Objects and @ObjectLevels arrays to walk the tree.
3249 # Arguments : $object - the GtkObject subclass.
3250 # @hierarchy - previous hierarchy
3251 #############################################################################
3254 my ($object,$hierarchy_ref) = @_;
3255 my @hierarchy = @{$hierarchy_ref};
3257 # Find object in the objects array.
3263 for ($i = 0; $i < @Objects; $i++) {
3265 if ($ObjectLevels[$i] <= $level) {
3268 elsif ($ObjectLevels[$i] == $level + 1) {
3269 push (@children, $Objects[$i]);
3272 elsif ($Objects[$i] eq $object) {
3275 $level = $ObjectLevels[$i];
3282 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3284 push (@ancestors, $object);
3285 #print "Level: $level\n";
3286 while ($level > 1) {
3288 if ($ObjectLevels[$j] < $level) {
3289 push (@ancestors, $Objects[$j]);
3290 $level = $ObjectLevels[$j];
3291 #print "Level: $level\n";
3295 # Output the ancestors, indented and with links.
3298 for ($i = $#ancestors; $i >= 0; $i--) {
3300 # Don't add a link to the current object, i.e. when i == 0.
3302 my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]);
3303 $link_text = "<link linkend=\"$ancestor_id\">$ancestors[$i]</link>";
3305 $link_text = "$ancestors[$i]";
3307 my $indented_text = ' ' x ($level * 4) . $link_text;
3308 # Check if we already have this object
3310 for ($j = 0; $j <= $#hierarchy; $j++) {
3311 if ($hierarchy[$j] eq $indented_text) {
3317 # We have a new entry, find insert position in alphabetical order
3318 my $indent = ' ' x ($level * 4);
3320 for ($j = $last_index; $j <= $#hierarchy; $j++) {
3321 if ($hierarchy[$j] !~ m/^${indent}/) {
3325 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3326 my $stripped_text = $hierarchy[$j];
3327 if ($indented_text !~ m/<link linkend/) {
3328 $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3329 $stripped_text =~ s%</link>%%;
3331 if ($indented_text lt $stripped_text) {
3339 $last_index = 1 + $#hierarchy;
3341 splice @hierarchy, $last_index, 0, ($indented_text);
3344 # Already have this one, remmeber index as base insert point
3345 $last_index = $index + 1;
3349 # Output the children, indented and with links.
3350 for ($i = 0; $i <= $#children; $i++) {
3351 my $id = &CreateValidSGMLID ($children[$i]);
3352 my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3353 splice @hierarchy, $last_index, 0, ($indented_text);
3360 #############################################################################
3361 # Function : GetInterfaces
3362 # Description : Returns the DocBook output describing the interfaces
3363 # implemented by a class. It uses the global %Interfaces hash.
3364 # Arguments : $object - the GtkObject subclass.
3365 #############################################################################
3372 # Find object in the objects array.
3373 if (exists($Interfaces{$object})) {
3374 my @ifaces = split(' ', $Interfaces{$object});
3379 for ($i = 0; $i <= $#ifaces; $i++) {
3380 my $id = &CreateValidSGMLID ($ifaces[$i]);
3381 $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3382 if ($i < $#ifaces - 1) {
3385 elsif ($i < $#ifaces) {
3400 #############################################################################
3401 # Function : GetImplementations
3402 # Description : Returns the DocBook output describing the implementations
3403 # of an interface. It uses the global %Interfaces hash.
3404 # Arguments : $object - the GtkObject subclass.
3405 #############################################################################
3407 sub GetImplementations {
3412 foreach my $key (keys %Interfaces) {
3413 if ($Interfaces{$key} =~ /\b$object\b/) {
3414 push (@impls, $key);
3418 @impls = sort @impls;
3421 $object is implemented by
3423 for ($i = 0; $i <= $#impls; $i++) {
3424 my $id = &CreateValidSGMLID ($impls[$i]);
3425 $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3426 if ($i < $#impls - 1) {
3429 elsif ($i < $#impls) {
3444 #############################################################################
3445 # Function : GetPrerequisites
3446 # Description : Returns the DocBook output describing the prerequisites
3447 # of an interface. It uses the global %Prerequisites hash.
3448 # Arguments : $iface - the interface.
3449 #############################################################################
3451 sub GetPrerequisites {
3456 if (exists($Prerequisites{$iface})) {
3461 my @prereqs = split(' ', $Prerequisites{$iface});
3462 for ($i = 0; $i <= $#prereqs; $i++) {
3463 my $id = &CreateValidSGMLID ($prereqs[$i]);
3464 $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3465 if ($i < $#prereqs - 1) {
3468 elsif ($i < $#prereqs) {
3482 #############################################################################
3483 # Function : GetDerived
3484 # Description : Returns the DocBook output describing the derived interfaces
3485 # of an interface. It uses the global %Prerequisites hash.
3486 # Arguments : $iface - the interface.
3487 #############################################################################
3495 foreach my $key (keys %Prerequisites) {
3496 if ($Prerequisites{$key} =~ /\b$iface\b/) {
3497 push (@derived, $key);
3500 if ($#derived >= 0) {
3501 @derived = sort @derived;
3504 $iface is required by
3506 for ($i = 0; $i <= $#derived; $i++) {
3507 my $id = &CreateValidSGMLID ($derived[$i]);
3508 $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3509 if ($i < $#derived - 1) {
3512 elsif ($i < $#derived) {
3527 #############################################################################
3528 # Function : GetSignals
3529 # Description : Returns the synopsis and detailed description DocBook output
3530 # for the signal handlers of a given GtkObject subclass.
3531 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3532 #############################################################################
3540 for ($i = 0; $i <= $#SignalObjects; $i++) {
3541 if ($SignalObjects[$i] eq $object) {
3542 #print "Found signal: $SignalNames[$i]\n";
3543 my $name = $SignalNames[$i];
3544 my $symbol = "${object}::${name}";
3545 my $id = &CreateValidSGMLID ("$object-$name");
3547 $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3548 $desc .= MakeIndexterms($symbol, $id);
3550 $desc .= OutputSymbolExtraLinks($symbol);
3552 $desc .= "<programlisting language=\"C\">";
3554 $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3555 my $type_modifier = defined($1) ? $1 : "";
3558 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3560 my $ret_type_output = "$type_modifier$xref$pointer";
3561 my $callback_name = "user_function";
3562 $desc .= "${ret_type_output}\n${callback_name} (";
3564 my $indentation = ' ' x (length($callback_name) + 2);
3565 my $pad = $indentation;
3567 my $sourceparams = $SourceSymbolParams{$symbol};
3568 my @params = split ("\n", $SignalPrototypes[$i]);
3571 my $type_len = length("gpointer");
3572 my $name_len = length("user_data");
3573 # do two passes, the first one is to calculate padding
3574 for ($l = 0; $l < 2; $l++) {
3575 for ($j = 0; $j <= $#params; $j++) {
3577 # allow alphanumerics, '_', '[' & ']' in param names
3578 if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3581 if (defined($sourceparams)) {
3582 $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3587 if (!defined($param_name)) {
3588 $param_name = "arg$j";
3591 if (length($type) + length($pointer) > $type_len) {
3592 $type_len = length($type) + length($pointer);
3594 if (length($param_name) > $name_len) {
3595 $name_len = length($param_name);
3599 $xref = &MakeXRef ($type, &tagify($type, "type"));
3600 $pad = ' ' x ($type_len - length($type) - length($pointer));
3601 $desc .= "$xref$pad $pointer${param_name},\n";
3602 $desc .= $indentation;
3605 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3606 "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3610 $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3611 $pad = ' ' x ($type_len - length("gpointer"));
3612 $desc .= "$xref$pad user_data)";
3613 $desc .= "</programlisting>\n";
3615 my $flags = $SignalFlags[$i];
3616 my $flags_string = "";
3618 if (defined ($flags)) {
3619 if ($flags =~ m/f/) {
3620 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3622 elsif ($flags =~ m/l/) {
3623 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3625 elsif ($flags =~ m/c/) {
3626 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3627 $flags_string = "Cleanup";
3629 if ($flags =~ m/r/) {
3630 if ($flags_string) { $flags_string .= " / "; }
3631 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3633 if ($flags =~ m/d/) {
3634 if ($flags_string) { $flags_string .= " / "; }
3635 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3637 if ($flags =~ m/a/) {
3638 if ($flags_string) { $flags_string .= " / "; }
3639 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3641 if ($flags =~ m/h/) {
3642 if ($flags_string) { $flags_string .= " / "; }
3643 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3647 $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";
3649 my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3650 my $parameters_output = 0;
3652 $AllSymbols{$symbol} = 1;
3653 if (defined ($SymbolDocs{$symbol})) {
3654 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3656 # Try to insert the parameter table at the author's desired
3657 # position. Otherwise we need to tag it onto the end.
3658 if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
3659 $parameters_output = 1;
3661 $desc .= $symbol_docs;
3663 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3664 $AllDocumentedSymbols{$symbol} = 1;
3667 $desc .= &MakeDeprecationNote($symbol);
3669 if ($parameters_output == 0) {
3670 $desc .= $parameters;
3672 if ($flags_string) {
3673 $desc .= "<para>Flags: $flags_string</para>\n";
3675 $desc .= OutputSymbolTraits ($symbol);
3676 $desc .= "</refsect2>";
3679 return ($synop, $desc);
3683 #############################################################################
3684 # Function : GetArgs
3685 # Description : Returns the synopsis and detailed description DocBook output
3686 # for the Args of a given GtkObject subclass.
3687 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3688 #############################################################################
3694 my $child_synop = "";
3695 my $child_desc = "";
3696 my $style_synop = "";
3697 my $style_desc = "";
3700 for ($i = 0; $i <= $#ArgObjects; $i++) {
3701 if ($ArgObjects[$i] eq $object) {
3702 #print "Found arg: $ArgNames[$i]\n";
3703 my $name = $ArgNames[$i];
3704 my $flags = $ArgFlags[$i];
3705 my $flags_string = "";
3709 if ($flags =~ m/c/) {
3710 $kind = "child property";
3713 elsif ($flags =~ m/s/) {
3714 $kind = "style property";
3721 # Remember only one colon so we don't clash with signals.
3722 my $symbol = "${object}:${name}";
3723 # use two dashes and ev. an extra separator here for the same reason.
3724 my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3726 my $type = $ArgTypes[$i];
3728 my $range = $ArgRanges[$i];
3729 my $range_output = CreateValidSGML ($range);
3730 my $default = $ArgDefaults[$i];
3731 my $default_output = CreateValidSGML ($default);
3733 if ($type eq "GtkString") {
3734 $type = "char *";
3736 if ($type eq "GtkSignal") {
3737 $type = "GtkSignalFunc, gpointer";
3738 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3739 . &MakeXRef ("gpointer");
3740 } elsif ($type =~ m/^(\w+)\*$/) {
3741 $type_output = &MakeXRef ($1, &tagify($1, "type")) . " *";
3743 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3746 if ($flags =~ m/r/) {
3747 $flags_string = "Read";
3749 if ($flags =~ m/w/) {
3750 if ($flags_string) { $flags_string .= " / "; }
3751 $flags_string .= "Write";
3753 if ($flags =~ m/x/) {
3754 if ($flags_string) { $flags_string .= " / "; }
3755 $flags_string .= "Construct";
3757 if ($flags =~ m/X/) {
3758 if ($flags_string) { $flags_string .= " / "; }
3759 $flags_string .= "Construct Only";
3762 $AllSymbols{$symbol} = 1;
3764 if (defined($SymbolDocs{$symbol}) &&
3765 !IsEmptyDoc($SymbolDocs{$symbol})) {
3766 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3767 #print ".. [$SymbolDocs{$symbol}][$blurb]\n";
3768 $AllDocumentedSymbols{$symbol} = 1;
3771 if (!($ArgBlurbs[$i] eq "")) {
3772 $AllDocumentedSymbols{$symbol} = 1;
3774 # FIXME: print a warning?
3775 #print ".. no description\n";
3777 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3780 my $pad1 = " " x (24 - length ($name));
3782 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";
3783 my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3784 $arg_desc .= MakeIndexterms($symbol, $id);
3786 $arg_desc .= OutputSymbolExtraLinks($symbol);
3788 $arg_desc .= "<programlisting> “$name”$pad1 $type_output</programlisting>\n";
3789 $arg_desc .= $blurb;
3790 $arg_desc .= &MakeDeprecationNote($symbol);
3792 if ($flags_string) {
3793 $arg_desc .= "<para>Flags: $flags_string</para>\n";
3796 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3798 if ($default ne "") {
3799 $arg_desc .= "<para>Default value: $default_output</para>\n";
3801 $arg_desc .= OutputSymbolTraits ($symbol);
3802 $arg_desc .= "</refsect2>\n";
3804 if ($flags =~ m/c/) {
3805 $child_synop .= $arg_synop;
3806 $child_desc .= $arg_desc;
3808 elsif ($flags =~ m/s/) {
3809 $style_synop .= $arg_synop;
3810 $style_desc .= $arg_desc;
3813 $synop .= $arg_synop;
3818 return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3822 #############################################################################
3823 # Function : ReadSourceDocumentation
3824 # Description : This reads in the documentation embedded in comment blocks
3825 # in the source code (for Gnome).
3827 # Parameter descriptions override any in the template files.
3828 # Function descriptions are placed before any description from
3829 # the template files.
3831 # It recursively descends the source directory looking for .c
3832 # files and scans them looking for specially-formatted comment
3835 # Arguments : $source_dir - the directory to scan.
3836 #############m###############################################################
3838 sub ReadSourceDocumentation {
3839 my ($source_dir) = @_;
3840 my ($file, $dir, @suffix_list, $suffix);
3842 # prepend entries from @SOURCE_DIR
3843 for my $dir (@SOURCE_DIRS) {
3844 # Check if the filename is in the ignore list.
3845 if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3846 @TRACE@("Skipping source directory: $source_dir");
3851 @TRACE@("Scanning source directory: $source_dir");
3853 # This array holds any subdirectories found.
3856 @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3858 opendir (SRCDIR, $source_dir)
3859 || die "Can't open source directory $source_dir: $!";
3861 foreach $file (readdir (SRCDIR)) {
3862 if ($file =~ /^\./) {
3864 } elsif (-d "$source_dir/$file") {
3865 push (@subdirs, $file);
3866 } elsif (@suffix_list) {
3867 foreach $suffix (@suffix_list) {
3868 if ($file =~ m/\.\Q${suffix}\E$/) {
3869 &ScanSourceFile ("$source_dir/$file");
3872 } elsif ($file =~ m/\.[ch]$/) {
3873 &ScanSourceFile ("$source_dir/$file");
3878 # Now recursively scan the subdirectories.
3879 foreach $dir (@subdirs) {
3880 &ReadSourceDocumentation ("$source_dir/$dir");
3885 #############################################################################
3886 # Function : ScanSourceFile
3887 # Description : Scans one source file looking for specially-formatted comment
3888 # blocks. Later &MergeSourceDocumentation is used to merge any
3889 # documentation found with the documentation already read in
3890 # from the template files.
3892 # Arguments : $file - the file to scan.
3893 #############################################################################
3895 sub ScanSourceFile {
3899 # prepend entries from @SOURCE_DIR
3900 for my $dir (@SOURCE_DIRS) {
3901 # Check if the filename is in the ignore list.
3902 if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3903 @TRACE@("Skipping source file: $file");
3908 if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3911 &LogWarning ($file, 1, "Can't find basename for this filename.");
3915 # Check if the basename is in the list of files to ignore.
3916 if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3917 @TRACE@("Skipping source file: $file");
3921 @TRACE@("Scanning source file: $file");
3923 open (SRCFILE, $file)
3924 || die "Can't open $file: $!";
3925 my $in_comment_block = 0;
3928 my ($description, $return_desc, $return_start, $return_style);
3929 my ($since_desc, $stability_desc, $deprecated_desc);
3931 my $ignore_broken_returns;
3934 # Look for the start of a comment block.
3935 if (!$in_comment_block) {
3936 if (m%^\s*/\*.*\*/%) {
3937 #one-line comment - not gtkdoc
3938 } elsif (m%^\s*/\*\*\s%) {
3939 #print "Found comment block start\n";
3941 $in_comment_block = 1;
3943 # Reset all the symbol data.
3950 $deprecated_desc = "";
3951 $stability_desc = "";
3952 $current_param = -1;
3953 $ignore_broken_returns = 0;
3959 # We're in a comment block. Check if we've found the end of it.
3962 # maybe its not even meant to be a gtk-doc comment?
3963 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3965 # Add the return value description onto the end of the params.
3967 push (@params, "Returns");
3968 push (@params, $return_desc);
3969 if ($return_style eq 'broken') {
3970 &LogWarning ($file, $., "Free-form return value description in $symbol. Use `Returns:' to avoid ambiguities.");
3973 # Convert special SGML characters
3974 $description = &ConvertSGMLChars ($symbol, $description);
3976 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3977 $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3980 # Handle Section docs
3981 if ($symbol =~ m/SECTION:\s*(.*)/) {
3985 if (scalar %KnownSymbols) {
3986 if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
3987 &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
3991 #print "SECTION DOCS found in source for : '$real_symbol'\n";
3992 $ignore_broken_returns = 1;
3993 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3994 #print " '".$params[$k]."'\n";
3995 $params[$k] = "\L$params[$k]";
3997 if ($params[$k] eq "short_description") {
3998 $key = "$TMPL_DIR/$real_symbol:Short_Description";
3999 } elsif ($params[$k] eq "see_also") {
4000 $key = "$TMPL_DIR/$real_symbol:See_Also";
4001 } elsif ($params[$k] eq "title") {
4002 $key = "$TMPL_DIR/$real_symbol:Title";
4003 } elsif ($params[$k] eq "stability") {
4004 $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4005 } elsif ($params[$k] eq "section_id") {
4006 $key = "$TMPL_DIR/$real_symbol:Section_Id";
4007 } elsif ($params[$k] eq "include") {
4008 $key = "$TMPL_DIR/$real_symbol:Include";
4009 } elsif ($params[$k] eq "image") {
4010 $key = "$TMPL_DIR/$real_symbol:Image";
4012 if (defined($key)) {
4013 $SourceSymbolDocs{$key}=$params[$k+1];
4014 $SourceSymbolSourceFile{$key} = $file;
4015 $SourceSymbolSourceLine{$key} = $.;
4018 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4019 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4020 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4021 #$SourceSymbolTypes{$symbol} = "SECTION";
4023 #print "SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n";
4024 $SourceSymbolDocs{$symbol} = $description;
4025 $SourceSymbolParams{$symbol} = [ @params ];
4026 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4027 #if (defined $DeclarationTypes{$symbol}) {
4028 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4030 $SourceSymbolSourceFile{$symbol} = $file;
4031 $SourceSymbolSourceLine{$symbol} = $.;
4035 ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4036 $since_desc =~ s/^\s+//;
4037 $since_desc =~ s/\s+$//;
4038 #print "Since($symbol) : [$since_desc]\n";
4039 $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4040 if(scalar @extra_lines) {
4041 &LogWarning ($file, $., "multi-line since docs found");
4045 if ($stability_desc) {
4046 $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4047 $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4050 if ($deprecated_desc) {
4051 if (!exists $Deprecated{$symbol}) {
4052 # don't warn for signals and properties
4053 #if ($symbol !~ m/::?(.*)/) {
4054 if (defined $DeclarationTypes{$symbol}) {
4055 &LogWarning ($file, $.,
4056 "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4057 " (See the --deprecated-guards option for gtkdoc-scan.)");
4060 $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4064 $in_comment_block = 0;
4068 # Get rid of ' * ' at start of every line in the comment block.
4070 # But make sure we don't get rid of the newline at the end.
4074 #print "DEBUG: scanning :$_";
4076 # If we haven't found the symbol name yet, look for it.
4078 if (m%^\s*(SECTION:\s*\S+)%) {
4080 #print "SECTION DOCS found in source for : '$symbol'\n";
4081 $ignore_broken_returns = 1;
4082 } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-a-z0-9_ ]+\)\s*)*$%) {
4084 #print "SYMBOL DOCS found in source for : '$symbol'\n";
4089 if ($in_part eq "description") {
4090 # Get rid of 'Description:'
4091 s%^\s*Description:%%;
4094 if (m/^\s*(returns:|return\s+value:)/i) {
4095 if ($return_style eq 'broken') {
4096 $description .= $return_start . $return_desc;
4099 if ($return_style eq 'sane') {
4100 &LogWarning ($file, $., "Multiple Returns for $symbol.");
4102 $return_style = 'sane';
4103 $ignore_broken_returns = 1;
4105 $in_part = "return";
4107 } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
4109 $return_style = 'broken';
4111 $in_part = "return";
4113 } elsif (m%^\s*since:%i) {
4114 # we're in param section and have not seen the blank line
4115 if($in_part ne "") {
4120 } elsif (m%^\s*deprecated:%i) {
4121 # we're in param section and have not seen the blank line
4122 if($in_part ne "") {
4123 $deprecated_desc = $';
4124 $in_part = "deprecated";
4127 } elsif (m%^\s*stability:%i) {
4128 $stability_desc = $';
4129 $in_part = "stability";
4133 if ($in_part eq "description") {
4136 } elsif ($in_part eq "return") {
4139 } elsif ($in_part eq "since") {
4142 } elsif ($in_part eq "stability") {
4143 $stability_desc .= $_;
4145 } elsif ($in_part eq "deprecated") {
4146 $deprecated_desc .= $_;
4150 # We must be in the parameters. Check for the empty line below them.
4152 $in_part = "description";
4156 # Look for a parameter name.
4157 if (m%^\s*@(\S+)\s*:\s*%) {
4158 my $param_name = $1;
4159 my $param_desc = $';
4161 #print "Found parameter: $param_name\n";
4162 # Allow varargs variations
4163 if ($param_name =~ m/^\.\.\.$/) {
4164 $param_name = "...";
4166 if ("\L$param_name" eq "returns") {
4167 $return_style = 'sane';
4168 $ignore_broken_returns = 1;
4170 @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4172 push (@params, $param_name);
4173 push (@params, $param_desc);
4174 $current_param += $PARAM_FIELD_COUNT;
4178 # We must be in the middle of a parameter description, so add it on
4179 # to the last element in @params.
4180 if ($current_param == -1) {
4181 &LogWarning ($file, $., "Parsing comment block file : parameter expected.");
4183 $params[$#params] .= $_;
4189 #############################################################################
4190 # Function : OutputMissingDocumentation
4191 # Description : Outputs report of documentation coverage to a file
4194 #############################################################################
4196 sub OutputMissingDocumentation {
4197 my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4198 my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4200 my $n_documented = 0;
4201 my $n_incomplete = 0;
4207 my $buffer_deprecated = "";
4208 my $buffer_descriptions = "";
4210 open(UNDOCUMENTED, ">$new_undocumented_file")
4211 || die "Can't create $new_undocumented_file";
4213 foreach $symbol (sort (keys (%AllSymbols))) {
4214 # FIXME: should we print LogWarnings for undocumented stuff?
4216 #my $ssfile = &GetSymbolSourceFile($symbol);
4217 #my $ssline = &GetSymbolSourceLine($symbol);
4218 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4220 if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4222 if (exists ($AllDocumentedSymbols{$symbol})) {
4224 if (exists ($AllIncompleteSymbols{$symbol})) {
4226 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4227 #$buffer .= "\t0: ".$location;
4229 } elsif (exists $Deprecated{$symbol}) {
4230 if (exists ($AllIncompleteSymbols{$symbol})) {
4232 $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4233 #$buffer .= "\t1a: ".$location;
4235 $buffer_deprecated .= $symbol . "\n";
4236 #$buffer .= "\t1b: ".$location;
4239 if (exists ($AllIncompleteSymbols{$symbol})) {
4241 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4242 #$buffer .= "\t2a: ".$location;
4244 $buffer .= $symbol . "\n";
4245 #$buffer .= "\t2b: ".$location;
4248 } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4250 #my $len1=(exists($SymbolDocs{$symbol}))?length($SymbolDocs{$symbol}):-1;
4251 #my $len2=(exists($AllDocumentedSymbols{$symbol}))?length($AllDocumentedSymbols{$symbol}):-1;
4252 #print "%%%% $symbol : $len1,$len2\n";
4253 if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4254 || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4257 # cut off the leading namespace ($TMPL_DIR)
4258 $symbol =~ m/^.*\/(.*)$/;
4259 $buffer_descriptions .= $1 . "\n";
4264 $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
4269 $percent = ($n_documented / $total) * 100.0;
4272 printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4273 print UNDOCUMENTED "$n_documented symbols documented.\n";
4274 print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4275 print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
4277 print UNDOCUMENTED $buffer;
4278 close (UNDOCUMENTED);
4280 return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4282 printf "%.0f%% symbol docs coverage", $percent;
4283 print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4284 print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4288 #############################################################################
4289 # Function : OutputUndeclaredSymbols
4290 # Description : Outputs symbols that are listed in the section file, but not
4291 # declaration is found in the sources
4294 #############################################################################
4296 sub OutputUndeclaredSymbols {
4297 my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4298 my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4300 open(UNDECLARED, ">$new_undeclared_file")
4301 || die "Can't create $new_undeclared_file";
4303 if (%UndeclaredSymbols) {
4304 print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4305 print UNDECLARED "\n";
4306 print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4310 return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4313 #############################################################################
4314 # Function : OutputUnusedSymbols
4315 # Description : Outputs symbols that are documented in comments, but not
4316 # declared in the sources
4319 #############################################################################
4321 sub OutputUnusedSymbols {
4323 my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4324 my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4326 open (UNUSED, ">$new_unused_file")
4327 || die "Can't open $new_unused_file";
4329 foreach $symbol (sort keys (%Declarations)) {
4330 if (!defined ($DeclarationOutput{$symbol})) {
4331 print (UNUSED "$symbol\n");
4335 foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4336 print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4340 if ($num_unused != 0) {
4341 &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4342 "They should be added to $MODULE-sections.txt in the appropriate place.");
4345 return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4349 #############################################################################
4350 # Function : OutputAllSymbols
4351 # Description : Outputs list of all symbols to a file
4354 #############################################################################
4356 sub OutputAllSymbols {
4357 my $n_documented = 0;
4363 open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4364 || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4366 foreach $symbol (sort (keys (%AllSymbols))) {
4367 print SYMBOLS $symbol . "\n";
4373 #############################################################################
4374 # Function : OutputSymbolsWithoutSince
4375 # Description : Outputs list of all symbols without a since tag to a file
4378 #############################################################################
4380 sub OutputSymbolsWithoutSince {
4381 my $n_documented = 0;
4387 open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4388 || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4390 foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4391 if (!defined $Since{$symbol}) {
4392 print SYMBOLS $symbol . "\n";
4400 #############################################################################
4401 # Function : MergeSourceDocumentation
4402 # Description : This merges documentation read from a source file into the
4403 # documentation read in from a template file.
4405 # Parameter descriptions override any in the template files.
4406 # Function descriptions are placed before any description from
4407 # the template files.
4410 #############################################################################
4412 sub MergeSourceDocumentation {
4416 if (scalar %SymbolDocs) {
4417 @Symbols=keys (%SymbolDocs);
4418 #print "num existing entries: ".(scalar @Symbols)."\n";
4419 #print " ",$Symbols[0], " ",$Symbols[1],"\n";
4422 # filter scanned declarations, with what we suppress from -sections.txt
4424 foreach $symbol (keys (%Declarations)) {
4425 if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4429 # , add the rest from -sections.txt
4430 foreach $symbol (keys (%KnownSymbols)) {
4431 if ($KnownSymbols{$symbol} == 1) {
4435 # and add whats found in the source
4436 foreach $symbol (keys (%SourceSymbolDocs)) {
4439 @Symbols = keys (%tmp);
4440 #print "num source entries: ".(scalar @Symbols)."\n";
4442 foreach $symbol (@Symbols) {
4443 $AllSymbols{$symbol} = 1;
4445 my $have_tmpl_docs = 0;
4447 ## see if the symbol is documented in template
4448 my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4449 my $check_tmpl_doc =$tmpl_doc;
4450 # remove all xml-tags and whitespaces
4451 $check_tmpl_doc =~ s/<.*?>//g;
4452 $check_tmpl_doc =~ s/\s//g;
4454 if ($check_tmpl_doc ne "") {
4455 $have_tmpl_docs = 1;
4456 #print "## [$check_tmpl_doc]\n";
4458 # if the docs have just an empty para, don't merge that.
4459 $check_tmpl_doc = $tmpl_doc;
4460 $check_tmpl_doc =~ s/(\s|\n)//msg;
4461 if ($check_tmpl_doc eq "<para></para>") {
4466 if (exists ($SourceSymbolDocs{$symbol})) {
4467 my $type = $DeclarationTypes {$symbol};
4469 #print "merging [$symbol] from source\n";
4471 my $item = "Parameter";
4472 if (defined ($type)) {
4473 if ($type eq 'STRUCT') {
4475 } elsif ($type eq 'ENUM') {
4477 } elsif ($type eq 'UNION') {
4484 my $src_doc = $SourceSymbolDocs{$symbol};
4485 # remove leading and training whitespaces
4486 $src_doc =~ s/^\s+//;
4487 $src_doc =~ s/\s+$//;
4489 # Don't output warnings for overridden titles as titles are
4490 # automatically generated in the -sections.txt file, and thus they
4491 # are often overridden.
4492 if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4493 # check if content is different
4494 if ($tmpl_doc ne $src_doc) {
4495 #print "[$tmpl_doc] [$src_doc]\n";
4496 &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4497 "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4501 if ($src_doc ne "") {
4502 $AllDocumentedSymbols{$symbol} = 1;
4505 # Convert <!--PARAMETERS--> with any blank lines around it to
4506 # a </para> followed by <!--PARAMETERS--> followed by <para>.
4507 $src_doc =~ s%\n+\s*<!--PARAMETERS-->\s*\n+%\n</para>\n<!--PARAMETERS-->\n<para>\n%g;
4509 # Do not add <para> to nothing, it breaks missing docs checks.
4510 my $src_doc_para = "";
4511 if ($src_doc ne "") {
4512 $src_doc_para = $src_doc;
4515 if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4516 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4517 } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4518 # For the title/summary/see also section docs we don't want to
4519 # add any <para> tags.
4520 $SymbolDocs{$symbol} = "$src_doc"
4522 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4526 if ($symbol =~ m/.*::.*/) {
4527 # For signals we prefer the param names from the source docs,
4528 # since the ones from the templates are likely to contain the
4529 # artificial argn names which are generated by gtkdoc-scangobj.
4530 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4531 # FIXME: we need to check for empty docs here as well!
4533 # The templates contain the definitive parameter names and order,
4534 # so we will not change that. We only override the actual text.
4535 my $tmpl_params = $SymbolParams{$symbol};
4536 if (!defined ($tmpl_params)) {
4537 #print "No merge needed for $symbol\n";
4538 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4539 # FIXME: we still like to get the number of params and merge
4540 # 1) we would noticed that params have been removed/renamed
4541 # 2) we would catch undocumented params
4542 # params are not (yet) exported in -decl.txt so that we
4543 # could easily grab them :/
4545 my $params = $SourceSymbolParams{$symbol};
4547 #print "Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n";
4548 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4549 my $tmpl_param_name = $$tmpl_params[$j];
4551 # Try to find the param in the source comment documentation.
4554 #print " try merge param $tmpl_param_name\n";
4555 for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4556 my $param_name = $$params[$k];
4557 my $param_desc = $$params[$k + 1];
4559 #print " test param $param_name\n";
4560 # We accept changes in case, since the Gnome source
4561 # docs contain a lot of these.
4562 if ("\L$param_name" eq "\L$tmpl_param_name") {
4565 # Override the description.
4566 $$tmpl_params[$j + 1] = $param_desc;
4568 # Set the name to "" to mark it as used.
4574 # If it looks like the parameters are there, but not
4575 # in the right place, try to explain a bit better.
4576 if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4577 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4578 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4582 # Now we output a warning if parameters have been described which
4584 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4585 my $param_name = $$params[$j];
4587 # the template builder cannot detect if a macro returns
4589 if(($type eq "MACRO") && ($param_name eq "Returns")) {
4590 # FIXME: do we need to add it then to tmpl_params[] ?
4591 my $num=$#$tmpl_params;
4592 #print " adding Returns: to macro docs for $symbol.\n";
4593 $$tmpl_params[$num+1]="Returns";
4594 $$tmpl_params[$num+2]=$$params[$j+1];
4597 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4598 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4604 if ($have_tmpl_docs) {
4605 $AllDocumentedSymbols{$symbol} = 1;
4606 #print "merging [$symbol] from template\n";
4609 #print "[$symbol] undocumented\n";
4613 # if this symbol is documented, check if docs are complete
4614 $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4615 # remove all xml-tags and whitespaces
4616 $check_tmpl_doc =~ s/<.*?>//g;
4617 $check_tmpl_doc =~ s/\s//g;
4618 if ($check_tmpl_doc ne "") {
4619 my $tmpl_params = $SymbolParams{$symbol};
4620 if (defined ($tmpl_params)) {
4621 my $type = $DeclarationTypes {$symbol};
4623 my $item = "Parameter";
4624 if (defined ($type)) {
4625 if ($type eq 'STRUCT') {
4627 } elsif ($type eq 'ENUM') {
4629 } elsif ($type eq 'UNION') {
4636 #print "Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n";
4638 if ($#$tmpl_params > 0) {
4640 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4641 # Output a warning if the parameter is empty and
4642 # remember for stats.
4643 my $tmpl_param_name = $$tmpl_params[$j];
4644 my $tmpl_param_desc = $$tmpl_params[$j + 1];
4645 if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4646 if (exists ($AllIncompleteSymbols{$symbol})) {
4647 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4649 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4651 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4652 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4657 if ($#$tmpl_params == 0) {
4658 $AllIncompleteSymbols{$symbol}="<items>";
4659 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4660 "$item descriptions for $symbol are missing in source code comment block.");
4662 # $#$tmpl_params==-1 means we don't know about parameters
4663 # this unfortunately does not tell if there should be some
4668 #print "num doc entries: ".(scalar %SymbolDocs)."\n";
4671 #############################################################################
4672 # Function : IsEmptyDoc
4673 # Description : Check if a doc-string is empty. Its also regarded as empty if
4674 # it only consist of whitespace or e.g. FIXME.
4675 # Arguments : the doc-string
4676 #############################################################################
4680 if ($doc =~ /^\s*$/) {
4684 if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4691 #############################################################################
4692 # Function : ConvertMarkDown
4693 # Description : Converts mark down syntax to the respective docbook.
4694 # http://de.wikipedia.org/wiki/Markdown
4695 # Inspired by the design of ParseDown
4696 # http://parsedown.org/
4697 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4698 # Arguments : the symbol name, the doc-string
4699 #############################################################################
4701 sub ConvertMarkDown {
4702 my ($symbol, $text) = @_;
4704 $text = &MarkDownParse ($text, $symbol);
4709 # SUPPORTED MARKDOWN
4710 # ==================
4719 # Setext-style Headers
4720 # --------------------
4728 # Ordered (unnested) Lists
4729 # ------------------------
4733 # 1. item 2 with loooong
4738 # Note: we require a blank line above the list items
4741 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4743 sub MarkDownParseBlocks {
4744 my ($linesref, $symbol, $context) = @_;
4747 my $md_block = { type => "" };
4749 OUTER: foreach $line (@$linesref) {
4750 my $first_char = substr ($line, 0, 1);
4751 my $deindented_line;
4753 if ($md_block->{"type"} eq "markup") {
4754 if (!$md_block->{"closed"}) {
4755 if (index ($line, $md_block->{"start"}) != -1) {
4756 $md_block->{"depth"}++;
4758 if (index ($line, $md_block->{"end"}) != -1) {
4759 if ($md_block->{"depth"} > 0) {
4760 $md_block->{"depth"}--;
4762 $md_block->{"closed"} = 1;
4765 $md_block->{"text"} .= "\n" . $line;
4770 $deindented_line = $line;
4771 $deindented_line =~ s/^\s+//;
4773 if ($md_block->{"type"} eq "heading") {
4774 # a heading is ended by any level less than or equal
4775 if ($md_block->{"level"} == 1) {
4776 if ($line =~ /^={4,}[ \t]*$/) {
4777 my $text = pop @{$md_block->{"lines"}};
4778 $md_block->{"interrupted"} = 0;
4779 push @md_blocks, $md_block;
4781 $md_block = { type => "heading",
4786 } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4787 $md_block->{"interrupted"} = 0;
4788 push @md_blocks, $md_block;
4790 $md_block = { type => "heading",
4797 # push lines into the block until the end is reached
4798 push @{$md_block->{"lines"}}, $line;
4802 if ($line =~ /^[=]{4,}[ \t]*$/) {
4803 my $text = pop @{$md_block->{"lines"}};
4804 $md_block->{"interrupted"} = 0;
4805 push @md_blocks, $md_block;
4807 $md_block = { type => "heading",
4812 } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4813 my $text = pop @{$md_block->{"lines"}};
4814 $md_block->{"interrupted"} = 0;
4815 push @md_blocks, $md_block;
4817 $md_block = { type => "heading",
4822 } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4823 $md_block->{"interrupted"} = 0;
4824 push @md_blocks, $md_block;
4826 $md_block = { type => "heading",
4830 level => length($1) };
4833 # push lines into the block until the end is reached
4834 push @{$md_block->{"lines"}}, $line;
4838 } elsif ($md_block->{"type"} eq "code") {
4839 if ($line =~ /^[ \t]*\]\|/) {
4840 push @md_blocks, $md_block;
4841 $md_block = { type => "paragraph",
4845 push @{$md_block->{"lines"}}, $line;
4850 if ($deindented_line eq "") {
4851 $md_block->{"interrupted"} = 1;
4855 if ($md_block->{"type"} eq "quote") {
4856 if (!$md_block->{"interrupted"}) {
4857 $line =~ s/^[ ]*>[ ]?//;
4858 push @{$md_block->{"lines"}}, $line;
4861 } elsif ($md_block->{"type"} eq "li") {
4862 my $marker = $md_block->{"marker"};
4863 if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4864 my $indentation = $1;
4865 if ($md_block->{"indentation"} ne $indentation) {
4866 push @{$md_block->{"lines"}}, $line;
4869 my $ordered = $md_block->{"ordered"};
4870 $lines =~ s/^[ ]{0,4}//;
4871 $md_block->{"last"} = 0;
4872 push @md_blocks, $md_block;
4873 $md_block = { type => "li",
4874 ordered => $ordered,
4875 indentation => $indentation,
4879 lines => [ $lines ] };
4884 if ($md_block->{"interrupted"}) {
4885 if ($first_char eq " ") {
4886 push @{$md_block->{"lines"}}, "";
4887 $line =~ s/^[ ]{0,4}//;
4888 push @{$md_block->{"lines"}}, $line;
4889 $md_block->{"interrupted"} = 0;
4893 $line =~ s/^[ ]{0,4}//;
4894 push @{$md_block->{"lines"}}, $line;
4899 # indentation sensitive types
4901 if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4903 push @md_blocks, $md_block;
4905 $md_block = { type => "heading",
4909 level => length($1) };
4912 } elsif ($line =~ /^={4,}[ \t]*$/) {
4913 # setext heading (====)
4915 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4916 push @md_blocks, $md_block;
4917 $md_block->{"type"} = "heading";
4918 $md_block->{"lines"} = [];
4919 $md_block->{"level"} = 1;
4923 } elsif ($line =~ /^-{4,}[ \t]*$/) {
4924 # setext heading (-----)
4926 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4927 push @md_blocks, $md_block;
4928 $md_block->{"type"} = "heading";
4929 $md_block->{"lines"} = [];
4930 $md_block->{"level"} = 2;
4934 } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4936 $md_block->{"interrupted"} = 1;
4937 push @md_blocks, $md_block;
4938 $md_block = { type => "code",
4944 # indentation insensitive types
4946 if ($line =~ /^[ ]*<!DOCTYPE/) {
4947 push @md_blocks, $md_block;
4949 $md_block = { type => "markup",
4950 text => $deindented_line,
4956 } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4957 # markup, including <?xml version="1.0"?>
4959 my $is_self_closing = defined($2);
4960 # FIXME: why do we need to skip https? here, if we generalize this to all
4961 # uri schemes we get parsing errors
4962 if (! $MD_TEXT_LEVEL_ELEMENTS{$tag} && $tag !~ /^https?/) {
4963 push @md_blocks, $md_block;
4965 if ($is_self_closing) {
4966 $md_block = { type => "self-closing tag",
4967 text => $deindented_line };
4968 $is_self_closing = 0;
4972 $md_block = { type => "markup",
4973 text => $deindented_line,
4974 start => "<" . $tag . ">",
4975 end => "</" . $tag . ">",
4978 if ($deindented_line =~ /<\/$tag>/) {
4979 $md_block->{"closed"} = 1;
4983 } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
4985 push @md_blocks, $md_block;
4987 my $indentation = $1;
4988 $lines =~ s/^[ ]{0,4}//;
4989 $md_block = { type => "li",
4991 indentation => $indentation,
4995 lines => [ $lines ] };
4997 } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
4998 push @md_blocks, $md_block;
4999 $md_block = { type => "quote",
5006 if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5007 push @md_blocks, $md_block;
5009 my $indentation = $1;
5010 $lines =~ s/^[ ]{0,4}//;
5012 $md_block = { type => "li",
5014 indentation => $indentation,
5015 marker => "\\d+[.]",
5018 lines => [ $lines ] };
5024 if ($md_block->{"type"} eq "paragraph") {
5025 if ($md_block->{"interrupted"}) {
5026 push @md_blocks, $md_block;
5027 $md_block = { type => "paragraph",
5031 $md_block->{"text"} .= "\n" . $line;
5034 push @md_blocks, $md_block;
5035 $md_block = { type => "paragraph",
5040 push @md_blocks, $md_block;
5047 sub MarkDownParseSpanElementsInner {
5048 my ($text, $markersref) = @_;
5050 my %markers = map { $_ => 1 } @$markersref;
5052 while ($text ne "") {
5053 my $closest_marker = "";
5054 my $closest_marker_index = 0;
5055 my $closest_marker_position = -1;
5056 my $text_marker = "";
5063 while ( ($marker, $use) = each %markers ) {
5064 my $marker_position;
5070 $marker_position = index ($text, $marker);
5072 if ($marker_position < 0) {
5073 $markers{$marker} = 0;
5077 if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5078 $closest_marker = $marker;
5079 $closest_marker_index = $i;
5080 $closest_marker_position = $marker_position;
5084 if ($closest_marker_position >= 0) {
5085 $text_marker = substr ($text, $closest_marker_position);
5088 if ($text_marker eq "") {
5094 $markup .= substr ($text, 0, $closest_marker_position);
5095 $text = substr ($text, $closest_marker_position);
5096 @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5098 if ($closest_marker eq "![" || $closest_marker eq "[") {
5101 if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5104 %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5107 $offset = length ($&);
5108 if ($element{"!"}) {
5112 $remaining_text = substr ($text, $offset);
5113 if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5118 $offset += length ($&);
5119 } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5120 $element{"ref"} = $1;
5121 $offset += length ($&);
5128 if ($element{"»"}) {
5129 $element{"»"} =~ s/&/&/g;
5130 $element{"»"} =~ s/</</g;
5132 if ($element{"!"}) {
5133 $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5135 if (defined ($element{"a"})) {
5136 $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5139 $markup .= "</inlinemediaobject>";
5140 } elsif ($element{"ref"}) {
5141 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5142 $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5144 if (defined ($element{"#"})) {
5145 # title attribute not supported
5148 $markup .= ">" . $element{"a"} . "</link>";
5150 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5151 $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5153 if (defined ($element{"#"})) {
5154 # title attribute not supported
5157 $markup .= ">" . $element{"a"} . "</ulink>";
5160 $markup .= $closest_marker;
5161 if ($closest_marker eq "![") {
5167 } elsif ($closest_marker eq "<") {
5168 if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5169 my $element_url = $1;
5170 $element_url =~ s/&/&/g;
5171 $element_url =~ s/</</g;
5173 $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5174 $offset = length ($&);
5175 } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5176 $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5177 $offset = length ($&);
5178 } elsif ($text =~ /^<[^>]+?>/) {
5180 $offset = length ($&);
5185 } elsif ($closest_marker eq "\\") {
5186 my $special_char = substr ($text, 1, 1);
5187 if ($MD_ESCAPABLE_CHARS{$special_char} ||
5188 $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5189 $markup .= $special_char;
5195 } elsif ($closest_marker eq "`") {
5196 if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5197 my $element_text = $2;
5198 $markup .= "<literal>" . $element_text . "</literal>";
5199 $offset = length ($&);
5204 } elsif ($closest_marker eq "@") {
5205 # Convert '@param()'
5206 # FIXME: we could make those also links ($symbol.$2), but that would be less
5207 # useful as the link target is a few lines up or down
5208 if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5209 $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5210 $offset = length ($&);
5211 } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5212 # Convert '@param', but not '\@param'.
5213 $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5214 $offset = length ($&);
5215 } elsif ($text =~ /^\\\@/) {
5217 $offset = length ($&);
5222 } elsif ($closest_marker eq "#") {
5223 if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5224 # handle #Object.func()
5225 $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5226 $offset = length ($&);
5227 } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5228 # Convert '#symbol', but not '\#symbol'.
5229 $markup .= $1 . &MakeHashXRef ($2, "type");
5230 $offset = length ($&);
5231 } elsif ($text =~ /^\\#/) {
5233 $offset = length ($&);
5238 } elsif ($closest_marker eq "%") {
5239 if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5240 # Convert '%constant', but not '\%constant'.
5241 # Also allow negative numbers, e.g. %-1.
5242 $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5243 $offset = length ($&);
5244 } elsif ($text =~ /^\\%/) {
5246 $offset = length ($&);
5254 $text = substr ($text, $offset);
5261 sub MarkDownParseSpanElements {
5263 my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5265 $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5267 # Convert 'function()' or 'macro()'.
5268 # if there is abc_*_def() we don't want to make a link to _def()
5269 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5270 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5275 sub ReplaceEntities {
5276 my ($text, $symbol) = @_;
5278 my @entities = ( [ "<", "<" ],
5287 [ "&", "&" ] ); # Do this last, or the others get messed up.
5290 # Expand entities in <programlisting> even inside CDATA since
5291 # we changed the definition of |[ to add CDATA
5292 for ($i = 0; $i <= $#entities; $i++) {
5293 if ($text =~ s/$entities[$i][0]/$entities[$i][1]/g) {
5294 # don't warn about * since it is expected to be present
5295 # for C-style comments
5296 if ($entities[$i][0] ne "*") {
5297 $warn .= "$entities[$i][0] ";
5304 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
5305 "Deprecated entities found in documentation for $symbol: $warn");
5311 sub MarkDownOutputDocBook {
5312 my ($blocksref, $symbol, $context) = @_;
5315 my @blocks = @$blocksref;
5317 foreach $block (@blocks) {
5321 if ($block->{"type"} eq "paragraph") {
5322 $text = &MarkDownParseSpanElements ($block->{"text"});
5323 if ($context eq "li" && $output eq "") {
5324 if ($block->{"interrupted"}) {
5325 $output .= "\n"."<para>".$text."</para>"."\n";
5327 $output .= "<para>".$text."</para>";
5333 $output .= "<para>".$text."</para>"."\n";
5336 } elsif ($block->{"type"} eq "heading") {
5339 $title = &MarkDownParseSpanElements ($block->{"text"});
5341 if ($block->{"level"} == 1) {
5347 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5348 if (defined ($block->{"id"})) {
5349 $output .= "<" . $tag . " id=\"" . $block->{"id"} . "\">";
5351 $output .= "<" . $tag . ">";
5354 $output .= "<title>" . $title . "</title>" . $text . "</" . $tag . ">\n";
5355 } elsif ($block->{"type"} eq "li") {
5356 my $tag = "itemizedlist";
5358 if ($block->{"first"}) {
5359 if ($block->{"ordered"}) {
5360 $tag = "orderedlist";
5362 $output .= "<".$tag.">\n";
5365 if ($block->{"interrupted"}) {
5366 push @{$block->{"lines"}}, "";
5369 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5370 $output .= "<listitem>".$text."</listitem>\n";
5371 if ($block->{"last"}) {
5372 if ($block->{"ordered"}) {
5373 $tag = "orderedlist";
5375 $output .= "</".$tag.">\n";
5377 } elsif ($block->{"type"} eq "quote") {
5378 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5379 $output .= "<blockquote>\n" . $text . "</blockquote>\n";
5380 } elsif ($block->{"type"} eq "code") {
5381 if ($block->{"language"}) {
5382 $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5384 $output .= "<informalexample><programlisting><![CDATA[\n";
5386 foreach (@{$block->{"lines"}}) {
5387 $output .= &ReplaceEntities ($_, $symbol) . "\n";
5389 $output .= "]]></programlisting></informalexample>\n";
5390 } elsif ($block->{"type"} eq "markup") {
5391 $text = &ExpandAbbreviations($symbol, $block->{"text"});
5392 $output .= $text."\n";
5394 $output .= $block->{"text"}."\n";
5401 sub MarkDownParseLines {
5402 my ($linesref, $symbol, $context) = @_;
5404 my @lines = @$linesref;
5407 @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5408 $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5414 my ($text, $symbol) = @_;
5417 # take out some variability in line endings
5418 $text =~ s%\r\n%\n%g;
5422 @lines = split("\n", $text);
5423 $text = MarkDownParseLines(\@lines, $symbol, "");
5428 #############################################################################
5429 # LIBRARY FUNCTIONS - These functions are used in both gtkdoc-mkdb and
5430 # gtkdoc-mktmpl and should eventually be moved to a
5432 #############################################################################
5434 #############################################################################
5435 # Function : ReadDeclarationsFile
5436 # Description : This reads in a file containing the function/macro/enum etc.
5439 # Note that in some cases there are several declarations with
5440 # the same name, e.g. for conditional macros. In this case we
5441 # set a flag in the %DeclarationConditional hash so the
5442 # declaration is not shown in the docs.
5444 # If a macro and a function have the same name, e.g. for
5445 # gtk_object_ref, the function declaration takes precedence.
5447 # Some opaque structs are just declared with 'typedef struct
5448 # _name name;' in which case the declaration may be empty.
5449 # The structure may have been found later in the header, so
5450 # that overrides the empty declaration.
5452 # Arguments : $file - the declarations file to read
5453 # $override - if declarations in this file should override
5454 # any current declaration.
5455 #############################################################################
5457 sub ReadDeclarationsFile {
5458 my ($file, $override) = @_;
5460 if ($override == 0) {
5462 %DeclarationTypes = ();
5463 %DeclarationConditional = ();
5464 %DeclarationOutput = ();
5468 || die "Can't open $file: $!";
5469 my $declaration_type = "";
5470 my $declaration_name;
5472 my $is_deprecated = 0;
5474 if (!$declaration_type) {
5475 if (m/^<([^>]+)>/) {
5476 $declaration_type = $1;
5477 $declaration_name = "";
5478 #print "Found declaration: $declaration_type\n";
5482 if (m%^<NAME>(.*)</NAME>%) {
5483 $declaration_name = $1;
5484 } elsif (m%^<DEPRECATED/>%) {
5486 } elsif (m%^</$declaration_type>%) {
5487 #print "Found end of declaration: $declaration_name\n";
5488 # Check that the declaration has a name
5489 if ($declaration_name eq "") {
5490 print "ERROR: $declaration_type has no name $file:$.\n";
5493 # If the declaration is an empty typedef struct _XXX XXX
5494 # set the flag to indicate the struct has a typedef.
5495 if ($declaration_type eq 'STRUCT'
5496 && $declaration =~ m/^\s*$/) {
5497 #print "Struct has typedef: $declaration_name\n";
5498 $StructHasTypedef{$declaration_name} = 1;
5501 # Check if the symbol is already defined.
5502 if (defined ($Declarations{$declaration_name})
5503 && $override == 0) {
5504 # Function declarations take precedence.
5505 if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5507 } elsif ($declaration_type eq 'FUNCTION') {
5508 if ($is_deprecated) {
5509 $Deprecated{$declaration_name} = "";
5511 $Declarations{$declaration_name} = $declaration;
5512 $DeclarationTypes{$declaration_name} = $declaration_type;
5513 } elsif ($DeclarationTypes{$declaration_name}
5514 eq $declaration_type) {
5515 # If the existing declaration is empty, or is just a
5516 # forward declaration of a struct, override it.
5517 if ($declaration_type eq 'STRUCT') {
5518 if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5519 if ($is_deprecated) {
5520 $Deprecated{$declaration_name} = "";
5522 $Declarations{$declaration_name} = $declaration;
5523 } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5524 # Ignore an empty or forward declaration.
5526 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5529 # set flag in %DeclarationConditional hash for
5530 # multiply defined macros/typedefs.
5531 $DeclarationConditional{$declaration_name} = 1;
5534 &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5537 if ($is_deprecated) {
5538 $Deprecated{$declaration_name} = "";
5540 $Declarations{$declaration_name} = $declaration;
5541 $DeclarationTypes{$declaration_name} = $declaration_type;
5544 $declaration_type = "";
5555 #############################################################################
5556 # Function : ReadSignalsFile
5557 # Description : This reads in an existing file which contains information on
5558 # all GTK signals. It creates the arrays @SignalNames and
5559 # @SignalPrototypes containing info on the signals. The first
5560 # line of the SignalPrototype is the return type of the signal
5561 # handler. The remaining lines are the parameters passed to it.
5562 # The last parameter, "gpointer user_data" is always the same
5563 # so is not included.
5564 # Arguments : $file - the file containing the signal handler prototype
5566 #############################################################################
5568 sub ReadSignalsFile {
5576 my $signal_prototype;
5578 # Reset the signal info.
5579 @SignalObjects = ();
5581 @SignalReturns = ();
5583 @SignalPrototypes = ();
5588 if (!open (INPUT, $file)) {
5589 warn "Can't open $file - skipping signals\n";
5596 $signal_object = "";
5598 $signal_returns = "";
5599 $signal_prototype = "";
5602 if (m/^<NAME>(.*)<\/NAME>/) {
5604 if ($signal_name =~ m/^(.*)::(.*)$/) {
5605 $signal_object = $1;
5606 ($signal_name = $2) =~ s/_/-/g;
5607 #print "Found signal: $signal_name\n";
5609 &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5611 } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5612 $signal_returns = $1;
5613 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5615 } elsif (m%^</SIGNAL>%) {
5616 #print "Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}";
5617 push (@SignalObjects, $signal_object);
5618 push (@SignalNames, $signal_name);
5619 push (@SignalReturns, $signal_returns);
5620 push (@SignalFlags, $signal_flags);
5621 push (@SignalPrototypes, $signal_prototype);
5624 $signal_prototype .= $_;
5632 #############################################################################
5633 # Function : ReadTemplateFile
5634 # Description : This reads in the manually-edited documentation file
5635 # corresponding to the file currently being created, so we can
5636 # insert the documentation at the appropriate places.
5637 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5638 # is a hash of arrays.
5639 # NOTE: This function is duplicated in gtkdoc-mktmpl (but
5640 # slightly different).
5641 # Arguments : $docsfile - the template file to read in.
5642 # $skip_unused_params - 1 if the unused parameters should be
5644 #############################################################################
5646 sub ReadTemplateFile {
5647 my ($docsfile, $skip_unused_params) = @_;
5649 my $template = "$docsfile.sgml";
5650 if (! -f $template) {
5651 #print "File doesn't exist: $template\n";
5655 # start with empty hashes, we merge the source comment for each file
5661 my $current_type = ""; # Type of symbol being read.
5662 my $current_symbol = ""; # Name of symbol being read.
5663 my $symbol_doc = ""; # Description of symbol being read.
5664 my @params; # Parameter names and descriptions of current
5665 # function/macro/function typedef.
5666 my $current_param = -1; # Index of parameter currently being read.
5667 # Note that the param array contains pairs
5668 # of param name & description.
5669 my $in_unused_params = 0; # True if we are reading in the unused params.
5670 my $in_deprecated = 0;
5672 my $in_stability = 0;
5674 open (DOCS, "$template")
5675 || die "Can't open $template: $!";
5677 @TRACE@("reading template $template");
5680 if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5683 if ($symbol eq "Title"
5684 || $symbol eq "Short_Description"
5685 || $symbol eq "Long_Description"
5686 || $symbol eq "See_Also"
5687 || $symbol eq "Stability_Level"
5688 || $symbol eq "Include"
5689 || $symbol eq "Image") {
5691 $symbol = $docsfile . ":" . $symbol;
5694 #print "Found symbol: $symbol\n";
5695 # Remember file and line for the symbol
5696 $SymbolSourceFile{$symbol} = $template;
5697 $SymbolSourceLine{$symbol} = $.;
5699 # Store previous symbol, but remove any trailing blank lines.
5700 if ($current_symbol ne "") {
5701 $symbol_doc =~ s/\s+$//;
5702 $SymbolTypes{$current_symbol} = $current_type;
5703 $SymbolDocs{$current_symbol} = $symbol_doc;
5705 # Check that the stability level is valid.
5706 if ($StabilityLevel{$current_symbol}) {
5707 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5710 if ($current_param >= 0) {
5711 $SymbolParams{$current_symbol} = [ @params ];
5713 # Delete any existing params in case we are overriding a
5714 # previously read template.
5715 delete $SymbolParams{$current_symbol};
5718 $current_type = $type;
5719 $current_symbol = $symbol;
5720 $current_param = -1;
5721 $in_unused_params = 0;
5728 } elsif (m/^<!-- # Unused Parameters # -->/) {
5729 #print "DEBUG: Found unused parameters\n";
5730 $in_unused_params = 1;
5733 } elsif ($in_unused_params && $skip_unused_params) {
5734 # When outputting the DocBook we skip unused parameters.
5735 #print "DEBUG: Skipping unused param: $_";
5739 # Check if param found. Need to handle "..." and "format...".
5740 if (s/^\@([\w\.]+):\040?//) {
5741 my $param_name = $1;
5742 my $param_desc = $_;
5743 # Allow variations of 'Returns'
5744 if ($param_name =~ m/^[Rr]eturns?$/) {
5745 $param_name = "Returns";
5747 # Allow varargs variations
5748 if ($param_name =~ m/^.*\.\.\.$/) {
5749 $param_name = "...";
5752 # strip trailing whitespaces and blank lines
5755 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5757 if ($param_name eq "Deprecated") {
5759 $Deprecated{$current_symbol} = $_;
5760 } elsif ($param_name eq "Since") {
5763 $Since{$current_symbol} = $_;
5764 } elsif ($param_name eq "Stability") {
5766 $StabilityLevel{$current_symbol} = $_;
5768 push (@params, $param_name);
5769 push (@params, $param_desc);
5770 $current_param += $PARAM_FIELD_COUNT;
5773 # strip trailing whitespaces and blank lines
5778 if ($in_deprecated) {
5779 $Deprecated{$current_symbol} .= $_;
5780 } elsif ($in_since) {
5781 &LogWarning ($template, $., "multi-line since docs found");
5782 #$Since{$current_symbol} .= $_;
5783 } elsif ($in_stability) {
5784 $StabilityLevel{$current_symbol} .= $_;
5785 } elsif ($current_param >= 0) {
5786 $params[$current_param] .= $_;
5795 # Remember to finish the current symbol doccs.
5796 if ($current_symbol ne "") {
5798 $symbol_doc =~ s/\s+$//;
5799 $SymbolTypes{$current_symbol} = $current_type;
5800 $SymbolDocs{$current_symbol} = $symbol_doc;
5802 # Check that the stability level is valid.
5803 if ($StabilityLevel{$current_symbol}) {
5804 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5807 if ($current_param >= 0) {
5808 $SymbolParams{$current_symbol} = [ @params ];
5810 # Delete any existing params in case we are overriding a
5811 # previously read template.
5812 delete $SymbolParams{$current_symbol};
5821 #############################################################################
5822 # Function : ReadObjectHierarchy
5823 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5824 # the GtkObject subclasses described in this module (and their
5826 # It places them in the @Objects array, and places their level
5827 # in the object hierarchy in the @ObjectLevels array, at the
5828 # same index. GtkObject, the root object, has a level of 1.
5830 # This also generates tree_index.sgml as it goes along.
5833 #############################################################################
5835 sub ReadObjectHierarchy {
5839 if (! -f $OBJECT_TREE_FILE) {
5842 if (!open (INPUT, $OBJECT_TREE_FILE)) {
5843 warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5847 # Only emit objects if they are supposed to be documented, or if
5848 # they have documented children. To implement this, we maintain a
5849 # stack of pending objects which will be emitted if a documented
5851 my @pending_objects = ();
5852 my @pending_levels = ();
5858 my $level = (length($`)) / 2 + 1;
5865 while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5866 my $pobject = pop(@pending_objects);
5867 my $plevel = pop(@pending_levels);
5870 push (@pending_objects, $object);
5871 push (@pending_levels, $level);
5873 if (exists($KnownSymbols{$object})) {
5874 while ($#pending_levels >= 0) {
5875 $object = shift @pending_objects;
5876 $level = shift @pending_levels;
5877 $xref = &MakeXRef ($object);
5879 push (@tree, ' ' x ($level * 4) . "$xref");
5880 push (@Objects, $object);
5881 push (@ObjectLevels, $level);
5882 $ObjectRoots{$object} = $root;
5886 # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5892 # FIXME: use $OUTPUT_FORMAT
5893 # my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.$OUTPUT_FORMAT";
5894 my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml";
5895 my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new";
5897 open (OUTPUT, ">$new_tree_index")
5898 || die "Can't create $new_tree_index: $!";
5900 if ($OUTPUT_FORMAT eq "xml") {
5901 my $tree_header = $doctype_header;
5903 $tree_header =~ s/<!DOCTYPE \w+/<!DOCTYPE screen/;
5904 print (OUTPUT "$tree_header");
5906 print (OUTPUT "<screen>\n" . &AddTreeLineArt(\@tree) . "\n</screen>\n");
5909 &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5914 #############################################################################
5915 # Function : ReadInterfaces
5916 # Description : This reads in the $MODULE.interfaces file.
5919 #############################################################################
5921 sub ReadInterfaces {
5924 if (! -f $INTERFACES_FILE) {
5927 if (!open (INPUT, $INTERFACES_FILE)) {
5928 warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5934 my ($object, @ifaces) = split;
5935 if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5936 my @knownIfaces = ();
5938 # filter out private interfaces, but leave foreign interfaces
5939 foreach my $iface (@ifaces) {
5940 if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5941 push (@knownIfaces, $iface);
5945 $Interfaces{$object} = join(' ', @knownIfaces);
5951 #############################################################################
5952 # Function : ReadPrerequisites
5953 # Description : This reads in the $MODULE.prerequisites file.
5956 #############################################################################
5958 sub ReadPrerequisites {
5959 %Prerequisites = ();
5961 if (! -f $PREREQUISITES_FILE) {
5964 if (!open (INPUT, $PREREQUISITES_FILE)) {
5965 warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
5971 my ($iface, @prereqs) = split;
5972 if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
5973 my @knownPrereqs = ();
5975 # filter out private prerequisites, but leave foreign prerequisites
5976 foreach my $prereq (@prereqs) {
5977 if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
5978 push (@knownPrereqs, $prereq);
5982 $Prerequisites{$iface} = join(' ', @knownPrereqs);
5988 #############################################################################
5989 # Function : ReadArgsFile
5990 # Description : This reads in an existing file which contains information on
5991 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
5992 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
5994 # Arguments : $file - the file containing the arg information.
5995 #############################################################################
6010 # Reset the args info.
6023 if (!open (INPUT, $file)) {
6024 warn "Can't open $file - skipping args\n";
6041 if (m/^<NAME>(.*)<\/NAME>/) {
6043 if ($arg_name =~ m/^(.*)::(.*)$/) {
6045 ($arg_name = $2) =~ s/_/-/g;
6046 #print "Found arg: $arg_name\n";
6048 &LogWarning ($file, $., "Invalid argument name: $arg_name");
6050 } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6052 } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6054 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6056 } elsif (m/^<NICK>(.*)<\/NICK>/) {
6058 } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6060 if ($arg_blurb eq "(null)") {
6062 &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6064 } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6066 } elsif (m%^</ARG>%) {
6067 #print "Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n";
6068 push (@ArgObjects, $arg_object);
6069 push (@ArgNames, $arg_name);
6070 push (@ArgTypes, $arg_type);
6071 push (@ArgRanges, $arg_range);
6072 push (@ArgFlags, $arg_flags);
6073 push (@ArgNicks, $arg_nick);
6074 push (@ArgBlurbs, $arg_blurb);
6075 push (@ArgDefaults, $arg_default);
6083 #############################################################################
6084 # Function : AddTreeLineArt
6085 # Description : Add unicode lineart to a pre-indented string array and returns
6086 # it as as multiline string.
6087 # Arguments : @tree - array of indented strings.
6088 #############################################################################
6090 sub AddTreeLineArt {
6091 my @tree = @{$_[0]};
6096 # iterate bottom up over the tree
6097 for ($i = $#tree; $i >= 0; $i--) {
6098 # count leading spaces
6099 $tree[$i] =~ /^([^<A-Za-z]*)/;
6100 $indent = length( $1 );
6101 # replace with ╰───, if place of ╰ is not space insert ├
6103 if (substr($tree[$i],$indent-4,1) eq " ") {
6104 substr($tree[$i],$indent-4,4) = "--- ";
6106 substr($tree[$i],$indent-4,4) = "+-- ";
6108 # go lines up while space and insert |
6109 for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6110 substr($tree[$j],$indent-4,1) = '|';
6115 my $res = join("\n", @tree);
6116 # unicode chars for: ╰──
6117 $res =~ s%---%<phrase role=\"lineart\">╰──</phrase>%g;
6118 # unicde chars for: ├──
6119 $res =~ s%\+--%<phrase role=\"lineart\">├──</phrase>%g;
6120 # unicode char for: │
6121 $res =~ s%\|%<phrase role=\"lineart\">│</phrase>%g;
6127 #############################################################################
6128 # Function : CheckIsObject
6129 # Description : Returns 1 if the given name is a GObject or a subclass.
6130 # It uses the global @Objects array.
6131 # Note that the @Objects array only contains classes in the
6132 # current module and their ancestors - not all GObject classes.
6133 # Arguments : $name - the name to check.
6134 #############################################################################
6138 my $root = $ObjectRoots{$name};
6139 # Let GBoxed pass as an object here to get -struct appended to the id
6140 # and prevent conflicts with sections.
6141 return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6145 #############################################################################
6146 # Function : MakeReturnField
6147 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6148 # Arguments : $str - the string to pad.
6149 #############################################################################
6151 sub MakeReturnField {
6154 return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6157 #############################################################################
6158 # Function : GetSymbolSourceFile
6159 # Description : Get the filename where the symbol docs where taken from.
6160 # Arguments : $symbol - the symbol name
6161 #############################################################################
6163 sub GetSymbolSourceFile {
6166 if (defined($SourceSymbolSourceFile{$symbol})) {
6167 return $SourceSymbolSourceFile{$symbol};
6168 } elsif (defined($SymbolSourceFile{$symbol})) {
6169 return $SymbolSourceFile{$symbol};
6175 #############################################################################
6176 # Function : GetSymbolSourceLine
6177 # Description : Get the file line where the symbol docs where taken from.
6178 # Arguments : $symbol - the symbol name
6179 #############################################################################
6181 sub GetSymbolSourceLine {
6184 if (defined($SourceSymbolSourceLine{$symbol})) {
6185 return $SourceSymbolSourceLine{$symbol};
6186 } elsif (defined($SymbolSourceLine{$symbol})) {
6187 return $SymbolSourceLine{$symbol};