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 A space-separated list of header files/dirs not to
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 @TRACE@(" ignore files: [$IGNORE_FILES]\n");
123 my ($empty_element_end, $doctype_header);
125 # autodetect output format
126 if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
127 if (!$MAIN_SGML_FILE) {
128 if (-e "${MODULE}-docs.xml") {
129 $OUTPUT_FORMAT = "xml";
131 $OUTPUT_FORMAT = "sgml";
134 if ($MAIN_SGML_FILE =~ m/.*\.(.*ml)$/i) {
135 $OUTPUT_FORMAT = lc($1);
140 $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
143 @TRACE@(" output-format: [$OUTPUT_FORMAT]\n");
145 if ($OUTPUT_FORMAT eq "xml") {
146 if (!$MAIN_SGML_FILE) {
147 # backwards compatibility
148 if (-e "${MODULE}-docs.sgml") {
149 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
151 $MAIN_SGML_FILE = "${MODULE}-docs.xml";
154 $empty_element_end = "/>";
156 if (-e $MAIN_SGML_FILE) {
157 open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
158 $doctype_header = "";
160 if (/^\s*<(book|chapter|article)/) {
161 # check that the top-level tag or the doctype decl contain the xinclude namespace decl
162 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
163 $doctype_header = "";
167 $doctype_header .= $_;
170 $doctype_header =~ s/<!DOCTYPE \w+/<!DOCTYPE refentry/;
171 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
172 # FIXME: not sure if we can do this now, as people already work-around the problem
173 # $doctype_header =~ s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#g;
176 "<?xml version=\"1.0\"?>\n" .
177 "<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.3//EN\"\n" .
178 " \"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd\"\n" .
180 " <!ENTITY % local.common.attrib \"xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'\">\n" .
184 if (!$MAIN_SGML_FILE) {
185 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
187 $empty_element_end = ">";
188 $doctype_header = "";
193 # All the files are written in subdirectories beneath here.
194 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
196 # This is where we put all the DocBook output.
197 $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/$OUTPUT_FORMAT";
199 # This file contains the object hierarchy.
200 my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
202 # This file contains the interfaces.
203 my $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
205 # This file contains the prerequisites.
206 my $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
208 # This file contains signal arguments and names.
209 my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
211 # The file containing Arg information.
212 my $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
214 # These global arrays store information on signals. Each signal has an entry
215 # in each of these arrays at the same index, like a multi-dimensional array.
216 my @SignalObjects; # The GtkObject which emits the signal.
217 my @SignalNames; # The signal name.
218 my @SignalReturns; # The return type.
219 my @SignalFlags; # Flags for the signal
220 my @SignalPrototypes; # The rest of the prototype of the signal handler.
222 # These global arrays store information on Args. Each Arg has an entry
223 # in each of these arrays at the same index, like a multi-dimensional array.
224 my @ArgObjects; # The GtkObject which has the Arg.
225 my @ArgNames; # The Arg name.
226 my @ArgTypes; # The Arg type - gint, GtkArrowType etc.
227 my @ArgFlags; # How the Arg can be used - readable/writable etc.
228 my @ArgNicks; # The nickname of the Arg.
229 my @ArgBlurbs; # Docstring of the Arg.
230 my @ArgDefaults; # Default value of the Arg.
231 my @ArgRanges; # The range of the Arg type
232 # These global hashes store declaration info keyed on a symbol name.
234 my %DeclarationTypes;
235 my %DeclarationConditional;
236 my %DeclarationOutput;
240 my %StructHasTypedef;
242 # These global hashes store the existing documentation.
246 my %SymbolSourceFile;
247 my %SymbolSourceLine;
248 my %SymbolAnnotations;
250 # These global hashes store documentation scanned from the source files.
251 my %SourceSymbolDocs;
252 my %SourceSymbolParams;
253 my %SourceSymbolSourceFile;
254 my %SourceSymbolSourceLine;
256 # all documentation goes in here, so we can do coverage analysis
258 my %AllIncompleteSymbols;
259 my %AllUnusedSymbols;
260 my %AllDocumentedSymbols;
262 # Undeclared yet documented symbols
263 my %UndeclaredSymbols;
265 # These global arrays store GObject, subclasses and the hierarchy (also of
266 # non-object derived types).
274 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
275 # section they are defined
280 # collects index entries
281 my %IndexEntriesFull;
282 my %IndexEntriesSince;
283 my %IndexEntriesDeprecated;
285 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
286 my %PreProcessorDirectives;
287 $PreProcessorDirectives{"assert"} = 1;
288 $PreProcessorDirectives{"define"} = 1;
289 $PreProcessorDirectives{"elif"} = 1;
290 $PreProcessorDirectives{"else"} = 1;
291 $PreProcessorDirectives{"endif"} = 1;
292 $PreProcessorDirectives{"error"} = 1;
293 $PreProcessorDirectives{"if"} = 1;
294 $PreProcessorDirectives{"ifdef"} = 1;
295 $PreProcessorDirectives{"ifndef"} = 1;
296 $PreProcessorDirectives{"include"} = 1;
297 $PreProcessorDirectives{"line"} = 1;
298 $PreProcessorDirectives{"pragma"} = 1;
299 $PreProcessorDirectives{"unassert"} = 1;
300 $PreProcessorDirectives{"undef"} = 1;
301 $PreProcessorDirectives{"warning"} = 1;
303 # remember used annotation (to write minimal glossary)
306 my %AnnotationDefinition = (
307 # the GObjectIntrospection annotations are defined at:
308 # https://live.gnome.org/GObjectIntrospection/Annotations
309 'allow-none' => "NULL is OK, both for passing and for returning.",
310 'nullable' => "NULL may be passed as the value in, out, in-out; or as a return value.",
311 'optional' => "NULL may be passed instead of a pointer to a location.",
312 'array' => "Parameter points to an array of items.",
313 'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
314 'attributes' => "Free-form key-value pairs.",
315 'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
316 'constructor' => "This symbol is a constructor, not a static method.",
317 'destroy' => "This parameter is a 'destroy_data', for callbacks.",
318 'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
319 'element-type' => "Generics and defining elements of containers and arrays.",
320 'error-domains' => "Typed errors. Similar to throws in Java.",
321 'foreign' => "This is a foreign struct.",
322 'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
323 'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
324 'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
325 'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
326 'method' => "This is a method",
327 'not-error' => "A GError parameter is not to be handled like a normal GError.",
328 'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
329 'out caller-allocates' => "Out parameter, where caller must allocate storage.",
330 'out callee-allocates' => "Out parameter, where caller must allocate storage.",
331 'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
332 'rename-to' => "Rename the original symbol's name to SYMBOL.",
333 'scope call' => "The callback is valid only during the call to the method.",
334 'scope async' => "The callback is valid until first called.",
335 'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
336 'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
337 'skip' => "Exposed in C code, not necessarily available in other languages.",
338 'transfer container' => "Free data container after the code is done.",
339 'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
340 'transfer full' => "Free data after the code is done.",
341 'transfer none' => "Don't free data after the code is done.",
342 'type' => "Override the parsed C type with given type.",
343 'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
344 'virtual' => "This is the invoker for a virtual method.",
345 'value' => "The specified value overrides the evaluated value of the constant.",
346 # Stability Level definition
347 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
349 The intention of a Stable interface is to enable arbitrary third parties to
350 develop applications to these interfaces, release them, and have confidence that
351 they will run on all minor releases of the product (after the one in which the
352 interface was introduced, and within the same major release). Even at a major
353 release, incompatible changes are expected to be rare, and to have strong
357 Unstable interfaces are experimental or transitional. They are typically used to
358 give outside developers early access to new or rapidly changing technology, or
359 to provide an interim solution to a problem where a more general solution is
360 anticipated. No claims are made about either source or binary compatibility from
361 one minor release to the next.
363 The Unstable interface level is a warning that these interfaces are subject to
364 change without warning and should not be used in unbundled products.
366 Given such caveats, customer impact need not be a factor when considering
367 incompatible changes to an Unstable interface in a major or minor release.
368 Nonetheless, when such changes are introduced, the changes should still be
369 mentioned in the release notes for the affected release.
372 An interface that can be used within the GNOME stack itself, but that is not
373 documented for end-users. Such functions should only be used in specified and
378 # Elements to consider non-block items in MarkDown parsing
379 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
391 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
407 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
410 # Create the root DocBook output directory if it doens't exist.
411 if (! -e $SGML_OUTPUT_DIR) {
412 mkdir ("$SGML_OUTPUT_DIR", 0777)
413 || die "Can't create directory: $SGML_OUTPUT_DIR";
416 # Function and other declaration output settings.
417 my $RETURN_TYPE_FIELD_WIDTH = 20;
418 my $SYMBOL_FIELD_WIDTH = 36;
419 my $MAX_SYMBOL_FIELD_WIDTH = 40;
420 my $SIGNAL_FIELD_WIDTH = 16;
421 my $PARAM_FIELD_COUNT = 2;
423 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
424 &ReadSignalsFile ($SIGNALS_FILE);
425 &ReadArgsFile ($ARGS_FILE);
426 &ReadObjectHierarchy;
430 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
431 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
432 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
435 for my $dir (@SOURCE_DIRS) {
436 &ReadSourceDocumentation ($dir);
439 my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt");
441 # If any of the DocBook SGML files have changed, update the timestamp file (so
442 # it can be used for Makefile dependencies).
443 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
445 # try to detect the common prefix
446 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
447 if ($NAME_SPACE eq "") {
454 foreach my $symbol (keys(%IndexEntriesFull)) {
455 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
456 if (length($symbol)>$pos) {
457 $letter=substr($symbol,$pos,1);
458 # stop prefix scanning
459 if ($letter eq "_") {
463 # Should we also stop on a uppercase char, if last was lowercase
464 # GtkWidget, if we have the 'W' and had the 't' before
465 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
466 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
467 # need to recound each time as this is per symbol
468 $prefix{uc($letter)}++;
472 if ($letter ne "" && $letter ne "_") {
475 foreach $letter (keys(%prefix)) {
476 #print "$letter: $prefix{$letter}.\n";
477 if ($prefix{$letter}>$maxsymbols) {
479 $maxsymbols=$prefix{$letter};
482 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
483 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
486 $NAME_SPACE .= $maxletter;
493 } while ($ratio > 0.9);
494 #print "most symbols start with $NAME_SPACE\n";
498 &OutputDeprecatedIndex;
500 &OutputAnnotationGlossary;
502 open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
503 || die "Can't create $ROOT_DIR/sgml.stamp: $!";
504 print (TIMESTAMP "timestamp");
508 #############################################################################
509 # Function : OutputObjectList
510 # Description : This outputs the alphabetical list of objects, in a columned
512 # FIXME: Currently this also outputs ancestor objects
513 # which may not actually be in this module.
515 #############################################################################
517 sub OutputObjectList {
520 # FIXME: use $OUTPUT_FORMAT
521 # my $old_object_index = "$SGML_OUTPUT_DIR/object_index.$OUTPUT_FORMAT";
522 my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml";
523 my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new";
525 open (OUTPUT, ">$new_object_index")
526 || die "Can't create $new_object_index: $!";
528 if ($OUTPUT_FORMAT eq "xml") {
529 my $header = $doctype_header;
531 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE informaltable/;
532 print (OUTPUT "$header");
535 print (OUTPUT <<EOF);
536 <informaltable pgwide="1" frame="none">
537 <tgroup cols="$cols">
538 <colspec colwidth="1*"${empty_element_end}
539 <colspec colwidth="1*"${empty_element_end}
540 <colspec colwidth="1*"${empty_element_end}
546 foreach $object (sort (@Objects)) {
547 my $xref = &MakeXRef ($object);
548 if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
549 print (OUTPUT "<entry>$xref</entry>\n");
550 if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
554 # emit an empty row, since empty tables are invalid
555 print (OUTPUT "<row><entry> </entry></row>\n");
558 if ($count % $cols > 0) {
559 print (OUTPUT "</row>\n");
563 print (OUTPUT <<EOF);
564 </tbody></tgroup></informaltable>
568 &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
571 #############################################################################
572 # Function : TrimTextBlock
573 # Description : Trims extra whitespace. Empty lines inside a block are
575 # Arguments : $desc - the text block to trim. May contain newlines.
576 #############################################################################
581 # strip leading spaces on the block
583 # strip trailing spaces on every line
584 $desc =~ s/\s+$/\n/mg;
590 #############################################################################
591 # Function : OutputSGML
592 # Description : This collects the output for each section of the docs, and
593 # outputs each file when the end of the section is found.
594 # Arguments : $file - the $MODULE-sections.txt file which contains all of
595 # the functions/macros/structs etc. being documented, organised
596 # into sections and subsections.
597 #############################################################################
602 @TRACE@("Reading: $file\n");
604 || die "Can't open $file: $!";
607 my $book_bottom = "";
608 my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
609 my $section_includes = "";
616 my $functions_synop = "";
617 my $other_synop = "";
618 my $functions_details = "";
619 my $other_details = "";
620 my $signals_synop = "";
621 my $signals_desc = "";
623 my $child_args_synop = "";
624 my $style_args_synop = "";
626 my $child_args_desc = "";
627 my $style_args_desc = "";
628 my $hierarchy_str = "";
631 my $implementations = "";
632 my $prerequisites = "";
634 my @file_objects = ();
636 my %symbol_def_line = ();
638 # merge the source docs, in case there are no templates
639 &MergeSourceDocumentation;
645 } elsif (m/^<SECTION>/) {
649 %symbol_def_line = ();
651 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
652 $other_synop .= "\n";
653 $functions_synop .= "\n";
656 } elsif (m/^<SUBSECTION>/) {
658 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
660 @TRACE@("Section: $title\n");
662 # We don't want warnings if object & class structs aren't used.
663 $DeclarationOutput{$title} = 1;
664 $DeclarationOutput{"${title}Class"} = 1;
665 $DeclarationOutput{"${title}Iface"} = 1;
666 $DeclarationOutput{"${title}Interface"} = 1;
668 } elsif (m/^<FILE>(.*)<\/FILE>/) {
670 if (! defined $templates{$filename}) {
671 if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
672 &MergeSourceDocumentation;
673 $templates{$filename}=$.;
676 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
677 "Previous occurrence on line ".$templates{$filename}.".");
679 if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
680 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
681 # Remove trailing blanks
685 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
687 $section_includes = $1;
689 if (defined $DEFAULT_INCLUDES) {
690 &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
697 } elsif (m/^<\/SECTION>/) {
698 @TRACE@("End of section: $title\n");
699 if ($num_symbols > 0) {
701 if ($OUTPUT_FORMAT eq "xml") {
702 $book_bottom .= " <xi:include href=\"xml/$filename.xml\"/>\n";
704 $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n";
705 $book_bottom .= " &$section_id;\n";
708 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
709 if ($section_includes) {
710 &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
712 $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
714 if ($section_includes eq "") {
715 $section_includes = $includes;
718 $signals_synop =~ s/^\n*//g;
719 $signals_synop =~ s/\n+$/\n/g;
720 if ($signals_synop ne '') {
721 $signals_synop = <<EOF;
722 <refsect1 id="$section_id.signals" role="signal_proto">
723 <title role="signal_proto.title">Signals</title>
724 <informaltable frame="none">
726 <colspec colname="signals_return" colwidth="150px"/>
727 <colspec colname="signals_name" colwidth="300px"/>
728 <colspec colname="signals_flags" colwidth="200px"/>
736 $signals_desc = TrimTextBlock($signals_desc);
737 $signals_desc = <<EOF;
738 <refsect1 id="$section_id.signal-details" role="signals">
739 <title role="signals.title">Signal Details</title>
745 $args_synop =~ s/^\n*//g;
746 $args_synop =~ s/\n+$/\n/g;
747 if ($args_synop ne '') {
749 <refsect1 id="$section_id.properties" role="properties">
750 <title role="properties.title">Properties</title>
751 <informaltable frame="none">
753 <colspec colname="properties_type" colwidth="150px"/>
754 <colspec colname="properties_name" colwidth="300px"/>
755 <colspec colname="properties_flags" colwidth="200px"/>
763 $args_desc = TrimTextBlock($args_desc);
765 <refsect1 id="$section_id.property-details" role="property_details">
766 <title role="property_details.title">Property Details</title>
772 $child_args_synop =~ s/^\n*//g;
773 $child_args_synop =~ s/\n+$/\n/g;
774 if ($child_args_synop ne '') {
775 $args_synop .= <<EOF;
776 <refsect1 id="$section_id.child-properties" role="child_properties">
777 <title role="child_properties.title">Child Properties</title>
778 <informaltable frame="none">
780 <colspec colname="child_properties_type" colwidth="150px"/>
781 <colspec colname="child_properties_name" colwidth="300px"/>
782 <colspec colname="child_properties_flags" colwidth="200px"/>
790 $child_args_desc = TrimTextBlock($child_args_desc);
792 <refsect1 id="$section_id.child-property-details" role="child_property_details">
793 <title role="child_property_details.title">Child Property Details</title>
799 $style_args_synop =~ s/^\n*//g;
800 $style_args_synop =~ s/\n+$/\n/g;
801 if ($style_args_synop ne '') {
802 $args_synop .= <<EOF;
803 <refsect1 id="$section_id.style-properties" role="style_properties">
804 <title role="style_properties.title">Style Properties</title>
805 <informaltable frame="none">
807 <colspec colname="style_properties_type" colwidth="150px"/>
808 <colspec colname="style_properties_name" colwidth="300px"/>
809 <colspec colname="style_properties_flags" colwidth="200px"/>
817 $style_args_desc = TrimTextBlock($style_args_desc);
819 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
820 <title role="style_properties_details.title">Style Property Details</title>
826 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
827 if ($hierarchy_str ne "") {
828 $hierarchy_str = <<EOF;
829 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
830 <title role="object_hierarchy.title">Object Hierarchy</title>
831 <screen>$hierarchy_str
837 $interfaces =~ TrimTextBlock($interfaces);
838 if ($interfaces ne "") {
840 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
841 <title role="impl_interfaces.title">Implemented Interfaces</title>
847 $implementations = TrimTextBlock($implementations);
848 if ($implementations ne "") {
849 $implementations = <<EOF;
850 <refsect1 id="$section_id.implementations" role="implementations">
851 <title role="implementations.title">Known Implementations</title>
857 $prerequisites = TrimTextBlock($prerequisites);
858 if ($prerequisites ne "") {
859 $prerequisites = <<EOF;
860 <refsect1 id="$section_id.prerequisites" role="prerequisites">
861 <title role="prerequisites.title">Prerequisites</title>
867 $derived = TrimTextBlock($derived);
868 if ($derived ne "") {
870 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
871 <title role="derived_interfaces.title">Known Derived Interfaces</title>
877 $functions_synop =~ s/^\n*//g;
878 $functions_synop =~ s/\n+$/\n/g;
879 if ($functions_synop ne '') {
880 $functions_synop = <<EOF;
881 <refsect1 id="$section_id.functions" role="functions_proto">
882 <title role="functions_proto.title">Functions</title>
883 <informaltable pgwide="1" frame="none">
885 <colspec colname="functions_return" colwidth="150px"/>
886 <colspec colname="functions_name"/>
896 $other_synop =~ s/^\n*//g;
897 $other_synop =~ s/\n+$/\n/g;
898 if ($other_synop ne '') {
899 $other_synop = <<EOF;
900 <refsect1 id="$section_id.other" role="other_proto">
901 <title role="other_proto.title">Types and Values</title>
902 <informaltable role="enum_members_table" pgwide="1" frame="none">
904 <colspec colname="name" colwidth="150px"/>
905 <colspec colname="description"/>
915 my $file_changed = &OutputSGMLFile ($filename, $title, $section_id,
917 \$functions_synop, \$other_synop,
918 \$functions_details, \$other_details,
919 \$signals_synop, \$signals_desc,
920 \$args_synop, \$args_desc,
921 \$hierarchy_str, \$interfaces,
923 \$prerequisites, \$derived,
933 $section_includes = "";
934 $functions_synop = "";
936 $functions_details = "";
941 $child_args_synop = "";
942 $style_args_synop = "";
944 $child_args_desc = "";
945 $style_args_desc = "";
949 $implementations = "";
953 } elsif (m/^(\S+)/) {
955 @TRACE@(" Symbol: $symbol in subsection: $subsection\n");
957 # check for duplicate entries
958 if (! defined $symbol_def_line{$symbol}) {
959 my $declaration = $Declarations{$symbol};
960 if (defined ($declaration)) {
961 if (&CheckIsObject ($symbol)) {
962 push @file_objects, $symbol;
964 # We don't want standard macros/functions of GObjects,
965 # or private declarations.
966 if ($subsection ne "Standard" && $subsection ne "Private") {
967 my ($synop, $desc) = &OutputDeclaration ($symbol,
969 my $type = $DeclarationTypes {$symbol};
971 if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
972 $functions_synop .= $synop;
973 $functions_details .= $desc;
974 } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
975 $functions_synop .= $synop;
976 $functions_details .= $desc;
978 $other_synop .= $synop;
979 $other_details .= $desc;
982 my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
983 my ($arg_synop, $child_arg_synop, $style_arg_synop,
984 $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
985 my $ifaces = &GetInterfaces ($symbol);
986 my $impls = &GetImplementations ($symbol);
987 my $prereqs = &GetPrerequisites ($symbol);
988 my $der = &GetDerived ($symbol);
989 @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
991 $signals_synop .= $sig_synop;
992 $signals_desc .= $sig_desc;
993 $args_synop .= $arg_synop;
994 $child_args_synop .= $child_arg_synop;
995 $style_args_synop .= $style_arg_synop;
996 $args_desc .= $arg_desc;
997 $child_args_desc .= $child_arg_desc;
998 $style_args_desc .= $style_arg_desc;
999 $interfaces .= $ifaces;
1000 $implementations .= $impls;
1001 $prerequisites .= $prereqs;
1004 # Note that the declaration has been output.
1005 $DeclarationOutput{$symbol} = 1;
1006 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
1007 $UndeclaredSymbols{$symbol} = 1;
1008 &LogWarning ($file, $., "No declaration found for $symbol.");
1011 $symbol_def_line{$symbol}=$.;
1013 if ($section_id eq "") {
1014 if($title eq "" && $filename eq "") {
1015 &LogWarning ($file, $., "Section has no title and no file.");
1017 # FIXME: one of those would be enough
1018 # filename should be an internal detail for gtk-doc
1021 } elsif ($filename eq "") {
1024 $filename =~ s/\s/_/g;
1026 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1027 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1028 # Remove trailing blanks and use as is
1029 $section_id =~ s/\s+$//;
1030 } elsif (&CheckIsObject ($title)) {
1031 # GObjects use their class name as the ID.
1032 $section_id = &CreateValidSGMLID ($title);
1034 $section_id = &CreateValidSGMLID ("$MODULE-$title");
1037 $SymbolSection{$symbol}=$title;
1038 $SymbolSectionId{$symbol}=$section_id;
1041 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1042 "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1048 &OutputMissingDocumentation;
1049 &OutputUndeclaredSymbols;
1050 &OutputUnusedSymbols;
1052 if ($OUTPUT_ALL_SYMBOLS) {
1055 if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1056 &OutputSymbolsWithoutSince;
1059 for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1060 my $file_changed = &OutputExtraFile ($filename);
1061 if ($file_changed) {
1066 &OutputBook ($book_top, $book_bottom);
1071 #############################################################################
1072 # Function : OutputIndex
1073 # Description : This writes an indexlist that can be included into the main-
1074 # document into an <index> tag.
1075 #############################################################################
1078 my ($basename, $apiindexref ) = @_;
1079 my %apiindex = %{$apiindexref};
1080 my $old_index = "$SGML_OUTPUT_DIR/$basename.xml";
1081 my $new_index = "$SGML_OUTPUT_DIR/$basename.new";
1082 my $lastletter = " ";
1087 open (OUTPUT, ">$new_index")
1088 || die "Can't create $new_index";
1090 my $header = $doctype_header;
1091 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE indexdiv/;
1093 print (OUTPUT "$header<indexdiv>\n");
1095 @TRACE@("generate $basename index (".%apiindex." entries)\n");
1097 # do a case insensitive sort while chopping off the prefix
1099 sort { $$a{criteria} cmp $$b{criteria} or $$a{original} cmp $$b{original} }
1100 map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1103 $symbol = $$hash{original};
1104 if (defined($$hash{short})) {
1105 $short_symbol = $$hash{short};
1107 $short_symbol = $symbol;
1110 # generate a short symbol description
1111 my $symbol_desc = "";
1112 my $symbol_section = "";
1113 my $symbol_section_id = "";
1114 my $symbol_type = "";
1115 if (defined($DeclarationTypes{$symbol})) {
1116 $symbol_type = lc($DeclarationTypes{$symbol});
1118 if ($symbol_type eq "") {
1119 @TRACE@("trying symbol $symbol\n");
1120 if ($symbol =~ m/(.*)::(.*)/) {
1124 @TRACE@(" trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1125 for ($i = 0; $i <= $#SignalNames; $i++) {
1126 if ($SignalNames[$i] eq $osym) {
1127 $symbol_type = "object signal";
1128 if (defined($SymbolSection{$oname})) {
1129 $symbol_section = $SymbolSection{$oname};
1130 $symbol_section_id = $SymbolSectionId{$oname};
1135 } elsif ($symbol =~ m/(.*):(.*)/) {
1139 @TRACE@(" trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1140 for ($i = 0; $i <= $#ArgNames; $i++) {
1141 @TRACE@(" ".$ArgNames[$i]."\n");
1142 if ($ArgNames[$i] eq $osym) {
1143 $symbol_type = "object property";
1144 if (defined($SymbolSection{$oname})) {
1145 $symbol_section = $SymbolSection{$oname};
1146 $symbol_section_id = $SymbolSectionId{$oname};
1153 if (defined($SymbolSection{$symbol})) {
1154 $symbol_section = $SymbolSection{$symbol};
1155 $symbol_section_id = $SymbolSectionId{$symbol};
1158 if ($symbol_type ne "") {
1159 $symbol_desc=", $symbol_type";
1160 if ($symbol_section ne "") {
1161 $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1162 #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1166 my $curletter = uc(substr($short_symbol,0,1));
1167 my $id = $apiindex{$symbol};
1169 @TRACE@(" add symbol $symbol with $id to index in section $curletter\n");
1171 if ($curletter ne $lastletter) {
1172 $lastletter = $curletter;
1174 if ($divopen == 1) {
1175 print (OUTPUT "</indexdiv>\n");
1177 print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1181 print (OUTPUT <<EOF);
1182 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1186 if ($divopen == 1) {
1187 print (OUTPUT "</indexdiv>\n");
1189 print (OUTPUT "</indexdiv>\n");
1192 &UpdateFileIfChanged ($old_index, $new_index, 0);
1196 #############################################################################
1197 # Function : OutputIndexFull
1198 # Description : This writes the full api indexlist that can be included into the
1199 # main document into an <index> tag.
1200 #############################################################################
1202 sub OutputIndexFull {
1203 &OutputIndex ("api-index-full", \%IndexEntriesFull);
1207 #############################################################################
1208 # Function : OutputDeprecatedIndex
1209 # Description : This writes the deprecated api indexlist that can be included
1210 # into the main document into an <index> tag.
1211 #############################################################################
1213 sub OutputDeprecatedIndex {
1214 &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1218 #############################################################################
1219 # Function : OutputSinceIndexes
1220 # Description : This writes the 'since' api indexlists that can be included into
1221 # the main document into an <index> tag.
1222 #############################################################################
1224 sub OutputSinceIndexes {
1225 my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1227 foreach my $version (@sinces) {
1228 @TRACE@("Since : [$version]\n");
1229 # TODO make filtered hash
1230 #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1231 my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1233 &OutputIndex ("api-index-$version", \%index);
1237 #############################################################################
1238 # Function : OutputAnnotationGlossary
1239 # Description : This writes a glossary of the used annotation terms into a
1240 # separate glossary file that can be included into the main
1242 #############################################################################
1244 sub OutputAnnotationGlossary {
1245 my $old_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.xml";
1246 my $new_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.new";
1247 my $lastletter = " ";
1250 # if there are no annotations used return
1251 return if (! keys(%AnnotationsUsed));
1253 # add acronyms that are referenced from acronym text
1255 foreach my $annotation (keys(%AnnotationsUsed)) {
1256 if(defined($AnnotationDefinition{$annotation})) {
1257 if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1258 if (!exists($AnnotationsUsed{$1})) {
1259 $AnnotationsUsed{$1} = 1;
1266 open (OUTPUT, ">$new_glossary")
1267 || die "Can't create $new_glossary";
1269 my $header = $doctype_header;
1270 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE glossary/;
1272 print (OUTPUT <<EOF);
1274 <glossary id="annotation-glossary">
1275 <title>Annotation Glossary</title>
1278 foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1279 if(defined($AnnotationDefinition{$annotation})) {
1280 my $def = $AnnotationDefinition{$annotation};
1281 my $curletter = uc(substr($annotation,0,1));
1283 if ($curletter ne $lastletter) {
1284 $lastletter = $curletter;
1286 if ($divopen == 1) {
1287 print (OUTPUT "</glossdiv>\n");
1289 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1292 print (OUTPUT <<EOF);
1294 <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1303 if ($divopen == 1) {
1304 print (OUTPUT "</glossdiv>\n");
1306 print (OUTPUT "</glossary>\n");
1309 &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1312 #############################################################################
1313 # Function : ReadKnownSymbols
1314 # Description : This collects the names of non-private symbols from the
1315 # $MODULE-sections.txt file.
1316 # Arguments : $file - the $MODULE-sections.txt file which contains all of
1317 # the functions/macros/structs etc. being documented, organised
1318 # into sections and subsections.
1319 #############################################################################
1321 sub ReadKnownSymbols {
1324 my $subsection = "";
1326 @TRACE@("Reading: $file\n");
1328 || die "Can't open $file: $!";
1334 } elsif (m/^<SECTION>/) {
1337 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1340 } elsif (m/^<SUBSECTION>/) {
1343 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1346 } elsif (m/^<FILE>(.*)<\/FILE>/) {
1347 $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1348 $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1351 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1354 } elsif (m/^<\/SECTION>/) {
1357 } elsif (m/^(\S+)/) {
1360 if ($subsection ne "Standard" && $subsection ne "Private") {
1361 $KnownSymbols{$symbol} = 1;
1364 $KnownSymbols{$symbol} = 0;
1372 #############################################################################
1373 # Function : OutputDeclaration
1374 # Description : Returns the synopsis and detailed description DocBook
1375 # describing one function/macro etc.
1376 # Arguments : $symbol - the name of the function/macro begin described.
1377 # $declaration - the declaration of the function/macro.
1378 #############################################################################
1380 sub OutputDeclaration {
1381 my ($symbol, $declaration) = @_;
1383 my $type = $DeclarationTypes {$symbol};
1384 if ($type eq 'MACRO') {
1385 return &OutputMacro ($symbol, $declaration);
1386 } elsif ($type eq 'TYPEDEF') {
1387 return &OutputTypedef ($symbol, $declaration);
1388 } elsif ($type eq 'STRUCT') {
1389 return &OutputStruct ($symbol, $declaration);
1390 } elsif ($type eq 'ENUM') {
1391 return &OutputEnum ($symbol, $declaration);
1392 } elsif ($type eq 'UNION') {
1393 return &OutputUnion ($symbol, $declaration);
1394 } elsif ($type eq 'VARIABLE') {
1395 return &OutputVariable ($symbol, $declaration);
1396 } elsif ($type eq 'FUNCTION') {
1397 return &OutputFunction ($symbol, $declaration, $type);
1398 } elsif ($type eq 'USER_FUNCTION') {
1399 return &OutputFunction ($symbol, $declaration, $type);
1401 die "Unknown symbol type";
1406 #############################################################################
1407 # Function : OutputSymbolTraits
1408 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1409 # Arguments : $symbol - the name of the function/macro begin described.
1410 #############################################################################
1412 sub OutputSymbolTraits {
1416 if (exists $Since{$symbol}) {
1417 $desc .= "<para role=\"since\">Since $Since{$symbol}</para>";
1419 if (exists $StabilityLevel{$symbol}) {
1420 my $stability = $StabilityLevel{$symbol};
1421 $AnnotationsUsed{$stability} = 1;
1422 $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1427 #############################################################################
1428 # Function : Output{Symbol,Section}ExtraLinks
1429 # Description : Returns extralinks for the symbol (if enabled).
1430 # Arguments : $symbol - the name of the function/macro begin described.
1431 #############################################################################
1435 return undef unless defined $text;
1437 # Build a char to hex map
1440 $escapes{chr($_)} = sprintf("%%%02X", $_);
1443 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1444 $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1449 sub OutputSymbolExtraLinks {
1453 if (0) { # NEW FEATURE: needs configurability
1454 my $sstr = &uri_escape($symbol);
1455 my $mstr = &uri_escape($MODULE);
1457 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1458 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$sstr">edit documentation</ulink>
1464 sub OutputSectionExtraLinks {
1465 my ($symbol,$docsymbol) = @_;
1468 if (0) { # NEW FEATURE: needs configurability
1469 my $sstr = &uri_escape($symbol);
1470 my $mstr = &uri_escape($MODULE);
1471 my $dsstr = &uri_escape($docsymbol);
1473 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1474 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$dsstr">edit documentation</ulink>
1481 #############################################################################
1482 # Function : OutputMacro
1483 # Description : Returns the synopsis and detailed description of a macro.
1484 # Arguments : $symbol - the macro.
1485 # $declaration - the declaration of the macro.
1486 #############################################################################
1489 my ($symbol, $declaration) = @_;
1490 my $id = &CreateValidSGMLID ($symbol);
1491 my $condition = &MakeConditionDescription ($symbol);
1492 my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1495 my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1496 my $title = $symbol . (@fields ? "()" : "");
1498 $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1499 $desc .= MakeIndexterms($symbol, $id);
1501 $desc .= OutputSymbolExtraLinks($symbol);
1504 $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1506 $synop .= "</entry></row>\n";
1508 # Don't output the macro definition if is is a conditional macro or it
1509 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1510 # longer than 2 lines, otherwise we get lots of complicated macros like
1512 if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1513 && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1514 my $decl_out = &CreateValidSGML ($declaration);
1515 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1517 $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1518 if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1520 my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1521 # Align each line so that if should all line up OK.
1522 $args =~ s/\n/\n$pad/gm;
1523 $desc .= &CreateValidSGML ($args);
1525 $desc .= "</programlisting>\n";
1528 $desc .= &MakeDeprecationNote($symbol);
1530 my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1532 if (defined ($SymbolDocs{$symbol})) {
1533 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1534 $desc .= $symbol_docs;
1537 $desc .= $parameters;
1538 $desc .= OutputSymbolTraits ($symbol);
1539 $desc .= "</refsect2>\n";
1540 return ($synop, $desc);
1544 #############################################################################
1545 # Function : OutputTypedef
1546 # Description : Returns the synopsis and detailed description of a typedef.
1547 # Arguments : $symbol - the typedef.
1548 # $declaration - the declaration of the typedef,
1549 # e.g. 'typedef unsigned int guint;'
1550 #############################################################################
1553 my ($symbol, $declaration) = @_;
1554 my $id = &CreateValidSGMLID ($symbol);
1555 my $condition = &MakeConditionDescription ($symbol);
1556 my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1557 my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1559 $desc .= MakeIndexterms($symbol, $id);
1561 $desc .= OutputSymbolExtraLinks($symbol);
1563 if (!defined ($DeclarationConditional{$symbol})) {
1564 my $decl_out = &CreateValidSGML ($declaration);
1565 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1568 $desc .= &MakeDeprecationNote($symbol);
1570 if (defined ($SymbolDocs{$symbol})) {
1571 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1573 $desc .= OutputSymbolTraits ($symbol);
1574 $desc .= "</refsect2>\n";
1575 return ($synop, $desc);
1579 #############################################################################
1580 # Function : OutputStruct
1581 # Description : Returns the synopsis and detailed description of a struct.
1582 # We check if it is a object struct, and if so we only output
1583 # parts of it that are noted as public fields.
1584 # We also use a different SGML ID for object structs, since the
1585 # original ID is used for the entire RefEntry.
1586 # Arguments : $symbol - the struct.
1587 # $declaration - the declaration of the struct.
1588 #############################################################################
1591 my ($symbol, $declaration) = @_;
1594 my $default_to_public = 1;
1595 if (&CheckIsObject ($symbol)) {
1596 @TRACE@("Found struct gtype: $symbol\n");
1598 $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1604 $id = &CreateValidSGMLID ($symbol . "_struct");
1605 $condition = &MakeConditionDescription ($symbol . "_struct");
1607 $id = &CreateValidSGMLID ($symbol);
1608 $condition = &MakeConditionDescription ($symbol);
1611 # Determine if it is a simple struct or it also has a typedef.
1612 my $has_typedef = 0;
1613 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1620 # For structs with typedefs we just output the struct name.
1622 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1624 $type_output = "struct";
1625 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1627 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1629 $desc .= MakeIndexterms($symbol, $id);
1631 $desc .= OutputSymbolExtraLinks($symbol);
1633 # Form a pretty-printed, private-data-removed form of the declaration
1636 if ($declaration =~ m/^\s*$/) {
1637 @TRACE@("Found opaque struct: $symbol\n");
1638 $decl_out = "typedef struct _$symbol $symbol;";
1639 } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1640 @TRACE@("Found opaque struct: $symbol\n");
1641 $decl_out = "struct $symbol;";
1643 my $public = $default_to_public;
1644 my $new_declaration = "";
1646 my $decl = $declaration;
1648 if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1649 my $struct_contents = $2;
1651 foreach $decl_line (split (/\n/, $struct_contents)) {
1652 @TRACE@("Struct line: $decl_line\n");
1653 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1655 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1658 $new_declaration .= $decl_line . "\n";
1662 if ($new_declaration) {
1663 # Strip any blank lines off the ends.
1664 $new_declaration =~ s/^\s*\n//;
1665 $new_declaration =~ s/\n\s*$/\n/;
1668 $decl_out = "typedef struct {\n" . $new_declaration
1671 $decl_out = "struct $symbol {\n" . $new_declaration
1676 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1677 "Couldn't parse struct:\n$declaration");
1680 # If we couldn't parse the struct or it was all private, output an
1681 # empty struct declaration.
1682 if ($decl_out eq "") {
1684 $decl_out = "typedef struct _$symbol $symbol;";
1686 $decl_out = "struct $symbol;";
1691 $decl_out = &CreateValidSGML ($decl_out);
1692 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1694 $desc .= &MakeDeprecationNote($symbol);
1696 if (defined ($SymbolDocs{$symbol})) {
1697 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1700 # Create a table of fields and descriptions
1702 # FIXME: Inserting  's into the produced type declarations here would
1703 # improve the output in most situations ... except for function
1704 # members of structs!
1705 my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1708 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1710 my $params = $SymbolParams{$symbol};
1712 # If no parameters are filled in, we don't generate the description
1713 # table, for backwards compatibility.
1716 if (defined $params) {
1717 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1718 if ($params->[$i] =~ /\S/) {
1726 my %field_descrs = @$params;
1727 my $missing_parameters = "";
1728 my $unused_parameters = "";
1731 <refsect3 role="struct_members">\n<title>Members</title>
1732 <informaltable role="struct_members_table" pgwide="1" frame="none">
1734 <colspec colname="struct_members_name" colwidth="300px"/>
1735 <colspec colname="struct_members_description"/>
1736 <colspec colname="struct_members_annotations" colwidth="200px"/>
1741 my $field_name = shift @fields;
1742 my $text = shift @fields;
1743 my $field_descr = $field_descrs{$field_name};
1744 my $param_annotations = "";
1746 $desc .= "<row><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1747 if (defined $field_descr) {
1748 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1749 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1751 $field_descr =~ s/^(\s|\n)+//msg;
1752 $field_descr =~ s/(\s|\n)+$//msg;
1753 $desc .= "<listitem>$field_descr</listitem>\n";
1754 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1755 delete $field_descrs{$field_name};
1757 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1758 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1759 if ($missing_parameters ne "") {
1760 $missing_parameters .= ", ".$field_name;
1762 $missing_parameters = $field_name;
1764 $desc .= "<entry /><entry />\n";
1766 $desc .= "</row>\n";
1768 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1769 foreach my $field_name (keys %field_descrs) {
1770 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1771 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1772 if ($unused_parameters ne "") {
1773 $unused_parameters .= ", ".$field_name;
1775 $unused_parameters = $field_name;
1779 # remember missing/unused parameters (needed in tmpl-free build)
1780 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1781 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1783 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1784 $AllUnusedSymbols{$symbol}=$unused_parameters;
1788 if (scalar(@fields) > 0) {
1789 if (! exists ($AllIncompleteSymbols{$symbol})) {
1790 $AllIncompleteSymbols{$symbol}="<items>";
1791 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1792 "Field descriptions for struct $symbol are missing in source code comment block.");
1793 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1798 $desc .= OutputSymbolTraits ($symbol);
1799 $desc .= "</refsect2>\n";
1800 return ($synop, $desc);
1804 #############################################################################
1805 # Function : OutputUnion
1806 # Description : Returns the synopsis and detailed description of a union.
1807 # Arguments : $symbol - the union.
1808 # $declaration - the declaration of the union.
1809 #############################################################################
1812 my ($symbol, $declaration) = @_;
1815 if (&CheckIsObject ($symbol)) {
1816 @TRACE@("Found union gtype: $symbol\n");
1823 $id = &CreateValidSGMLID ($symbol . "_union");
1824 $condition = &MakeConditionDescription ($symbol . "_union");
1826 $id = &CreateValidSGMLID ($symbol);
1827 $condition = &MakeConditionDescription ($symbol);
1830 # Determine if it is a simple struct or it also has a typedef.
1831 my $has_typedef = 0;
1832 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1839 # For unions with typedefs we just output the union name.
1841 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1843 $type_output = "union";
1844 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1846 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1848 $desc .= MakeIndexterms($symbol, $id);
1850 $desc .= OutputSymbolExtraLinks($symbol);
1851 $desc .= &MakeDeprecationNote($symbol);
1853 if (defined ($SymbolDocs{$symbol})) {
1854 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1857 # Create a table of fields and descriptions
1859 # FIXME: Inserting  's into the produced type declarations here would
1860 # improve the output in most situations ... except for function
1861 # members of structs!
1862 my @fields = ParseStructDeclaration($declaration, 0,
1865 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1867 my $params = $SymbolParams{$symbol};
1869 # If no parameters are filled in, we don't generate the description
1870 # table, for backwards compatibility
1873 if (defined $params) {
1874 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1875 if ($params->[$i] =~ /\S/) {
1883 my %field_descrs = @$params;
1884 my $missing_parameters = "";
1885 my $unused_parameters = "";
1888 <refsect3 role="union_members">\n<title>Members</title>
1889 <informaltable role="union_members_table" pgwide="1" frame="none">
1891 <colspec colname="union_members_name" colwidth="300px"/>
1892 <colspec colname="union_members_description"/>
1893 <colspec colname="union_members_annotations" colwidth="200px"/>
1898 my $field_name = shift @fields;
1899 my $text = shift @fields;
1900 my $field_descr = $field_descrs{$field_name};
1901 my $param_annotations = "";
1903 $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1904 if (defined $field_descr) {
1905 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1906 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1909 $field_descr =~ s/^(\s|\n)+//msg;
1910 $field_descr =~ s/(\s|\n)+$//msg;
1911 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1912 delete $field_descrs{$field_name};
1914 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1915 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1916 if ($missing_parameters ne "") {
1917 $missing_parameters .= ", ".$field_name;
1919 $missing_parameters = $field_name;
1921 $desc .= "<entry /><entry />\n";
1923 $desc .= "</row>\n";
1925 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1926 foreach my $field_name (keys %field_descrs) {
1927 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1928 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1929 if ($unused_parameters ne "") {
1930 $unused_parameters .= ", ".$field_name;
1932 $unused_parameters = $field_name;
1936 # remember missing/unused parameters (needed in tmpl-free build)
1937 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1938 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1940 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1941 $AllUnusedSymbols{$symbol}=$unused_parameters;
1945 if (scalar(@fields) > 0) {
1946 if (! exists ($AllIncompleteSymbols{$symbol})) {
1947 $AllIncompleteSymbols{$symbol}="<items>";
1948 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1949 "Field descriptions for union $symbol are missing in source code comment block.");
1950 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1955 $desc .= OutputSymbolTraits ($symbol);
1956 $desc .= "</refsect2>\n";
1957 return ($synop, $desc);
1961 #############################################################################
1962 # Function : OutputEnum
1963 # Description : Returns the synopsis and detailed description of a enum.
1964 # Arguments : $symbol - the enum.
1965 # $declaration - the declaration of the enum.
1966 #############################################################################
1969 my ($symbol, $declaration) = @_;
1972 if (&CheckIsObject ($symbol)) {
1973 @TRACE@("Found enum gtype: $symbol\n");
1980 $id = &CreateValidSGMLID ($symbol . "_enum");
1981 $condition = &MakeConditionDescription ($symbol . "_enum");
1983 $id = &CreateValidSGMLID ($symbol);
1984 $condition = &MakeConditionDescription ($symbol);
1987 my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1988 my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1990 $desc .= MakeIndexterms($symbol, $id);
1992 $desc .= OutputSymbolExtraLinks($symbol);
1993 $desc .= &MakeDeprecationNote($symbol);
1995 if (defined ($SymbolDocs{$symbol})) {
1996 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1999 # Create a table of fields and descriptions
2001 my @fields = ParseEnumDeclaration($declaration);
2002 my $params = $SymbolParams{$symbol};
2004 # If nothing at all is documented log a single summary warning at the end.
2005 # Otherwise, warn about each undocumented item.
2008 if (defined $params) {
2009 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
2010 if ($params->[$i] =~ /\S/) {
2017 my %field_descrs = (defined $params ? @$params : ());
2018 my $missing_parameters = "";
2019 my $unused_parameters = "";
2022 <refsect3 role="enum_members">\n<title>Members</title>
2023 <informaltable role="enum_members_table" pgwide="1" frame="none">
2025 <colspec colname="enum_members_name" colwidth="300px"/>
2026 <colspec colname="enum_members_description"/>
2027 <colspec colname="enum_members_annotations" colwidth="200px"/>
2031 for my $field_name (@fields) {
2032 my $field_descr = $field_descrs{$field_name};
2033 my $param_annotations = "";
2035 $id = &CreateValidSGMLID ($field_name);
2036 $condition = &MakeConditionDescription ($field_name);
2037 $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2038 if (defined $field_descr) {
2039 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2040 $field_descr = &ConvertMarkDown($symbol, $field_descr);
2041 $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2042 delete $field_descrs{$field_name};
2045 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2046 "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2047 if ($missing_parameters ne "") {
2048 $missing_parameters .= ", ".$field_name;
2050 $missing_parameters = $field_name;
2053 $desc .= "<entry /><entry />\n";
2055 $desc .= "</row>\n";
2057 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2058 foreach my $field_name (keys %field_descrs) {
2059 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2060 "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2061 if ($unused_parameters ne "") {
2062 $unused_parameters .= ", ".$field_name;
2064 $unused_parameters = $field_name;
2068 # remember missing/unused parameters (needed in tmpl-free build)
2069 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2070 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2072 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2073 $AllUnusedSymbols{$symbol}=$unused_parameters;
2077 if (scalar(@fields) > 0) {
2078 if (! exists ($AllIncompleteSymbols{$symbol})) {
2079 $AllIncompleteSymbols{$symbol}="<items>";
2080 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2081 "Value descriptions for $symbol are missing in source code comment block.");
2086 $desc .= OutputSymbolTraits ($symbol);
2087 $desc .= "</refsect2>\n";
2088 return ($synop, $desc);
2092 #############################################################################
2093 # Function : OutputVariable
2094 # Description : Returns the synopsis and detailed description of a variable.
2095 # Arguments : $symbol - the extern'ed variable.
2096 # $declaration - the declaration of the variable.
2097 #############################################################################
2099 sub OutputVariable {
2100 my ($symbol, $declaration) = @_;
2101 my $id = &CreateValidSGMLID ($symbol);
2102 my $condition = &MakeConditionDescription ($symbol);
2104 @TRACE@("ouputing variable: '$symbol' '$declaration'");
2107 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*;/) {
2108 my $mod1 = defined ($1) ? $1 : "";
2109 my $ptr = defined ($3) ? $3 : "";
2110 my $space = defined ($4) ? $4 : "";
2111 my $mod2 = defined ($5) ? $5 : "";
2112 $type_output = "extern $mod1$ptr$space$mod2";
2113 } 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*=/) {
2114 my $mod1 = defined ($1) ? $1 : "";
2115 my $ptr = defined ($3) ? $3 : "";
2116 my $space = defined ($4) ? $4 : "";
2117 my $mod2 = defined ($5) ? $5 : "";
2118 $type_output = "$mod1$ptr$space$mod2";
2120 $type_output = "extern";
2122 my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2124 my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2126 $desc .= MakeIndexterms($symbol, $id);
2128 $desc .= OutputSymbolExtraLinks($symbol);
2130 my $decl_out = &CreateValidSGML ($declaration);
2131 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2133 $desc .= &MakeDeprecationNote($symbol);
2135 if (defined ($SymbolDocs{$symbol})) {
2136 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2138 $desc .= OutputSymbolTraits ($symbol);
2139 $desc .= "</refsect2>\n";
2140 return ($synop, $desc);
2144 #############################################################################
2145 # Function : OutputFunction
2146 # Description : Returns the synopsis and detailed description of a function.
2147 # Arguments : $symbol - the function.
2148 # $declaration - the declaration of the function.
2149 #############################################################################
2151 sub OutputFunction {
2152 my ($symbol, $declaration, $symbol_type) = @_;
2153 my $id = &CreateValidSGMLID ($symbol);
2154 my $condition = &MakeConditionDescription ($symbol);
2156 # Take out the return type $1 $2 $3
2157 $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//;
2158 my $type_modifier = defined($1) ? $1 : "";
2161 # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2162 $pointer =~ s/\s+$//;
2163 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2165 #if ($symbol_type eq 'USER_FUNCTION') {
2166 # $start = "typedef ";
2169 # We output const rather than G_CONST_RETURN.
2170 $type_modifier =~ s/G_CONST_RETURN/const/g;
2171 $pointer =~ s/G_CONST_RETURN/const/g;
2172 $pointer =~ s/^\s+/ /g;
2174 my $ret_type_output;
2175 $ret_type_output = "$start$type_modifier$xref$pointer\n";
2178 $indent_len = length ($symbol) + 2;
2179 my $char1 = my $char2 = my $char3 = "";
2180 if ($symbol_type eq 'USER_FUNCTION') {
2182 $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2184 $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2187 my ($symbol_output, $symbol_desc_output);
2188 $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2189 if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2190 $symbol_desc_output = "$char1$char2$symbol$char3 ";
2192 $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2193 $symbol_desc_output = "$char1$char2$symbol$char3\n"
2194 . (' ' x ($indent_len - 1));
2197 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";
2199 my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol} ()</title>\n";
2201 $desc .= MakeIndexterms($symbol, $id);
2203 $desc .= OutputSymbolExtraLinks($symbol);
2205 $desc .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2207 my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2209 &tagify($_[0],"parameter");
2212 for (my $i = 1; $i <= $#fields; $i += 2) {
2213 my $field_name = $fields[$i];
2216 $desc .= "$field_name";
2219 . (' ' x $indent_len)
2225 $desc .= ");</programlisting>\n";
2227 $desc .= &MakeDeprecationNote($symbol);
2229 if (defined ($SymbolDocs{$symbol})) {
2230 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2231 $desc .= $symbol_docs;
2233 if (defined ($SymbolAnnotations{$symbol})) {
2234 my $param_desc = $SymbolAnnotations{$symbol};
2235 my $param_annotations = "";
2236 @TRACE@("expand annotation for $symbol: $param_desc");
2237 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2238 @TRACE@("expanded annotation for $symbol: $param_desc | $param_annotations");
2239 if ($param_annotations ne "") {
2240 $desc .= "\n<para>$param_annotations</para>";
2244 $desc .= &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2245 $desc .= OutputSymbolTraits ($symbol);
2246 $desc .= "</refsect2>\n";
2247 return ($synop, $desc);
2251 #############################################################################
2252 # Function : OutputParamDescriptions
2253 # Description : Returns the DocBook output describing the parameters of a
2254 # function, macro or signal handler.
2255 # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2256 # handlers have an implicit user_data parameter last.
2257 # $symbol - the name of the function/macro being described.
2258 # @fields - parsed fields from the declaration, used to determine
2259 # undocumented/unused entries
2260 #############################################################################
2262 sub OutputParamDescriptions {
2263 my ($symbol_type, $symbol, @fields) = @_;
2265 my $params = $SymbolParams{$symbol};
2267 my %field_descrs = ();
2270 %field_descrs = @fields;
2271 delete $field_descrs{"void"};
2272 delete $field_descrs{"Returns"};
2275 if (defined $params) {
2277 my $params_desc = "";
2278 my $missing_parameters = "";
2279 my $unused_parameters = "";
2282 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2283 my $param_name = $$params[$j];
2284 my $param_desc = $$params[$j + 1];
2285 my $param_annotations = "";
2287 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2288 $param_desc = &ConvertMarkDown($symbol, $param_desc);
2290 $param_desc =~ s/^(\s|\n)+//msg;
2291 $param_desc =~ s/(\s|\n)+$//msg;
2292 if ($param_name eq "Returns") {
2293 $returns = $param_desc;
2294 if ($param_annotations ne "") {
2295 $returns .= "\n<para>$param_annotations</para>";
2297 } elsif ($param_name eq "void") {
2298 # FIXME: &LogWarning()?
2299 @TRACE@("!!!! void in params for $symbol?\n");
2302 if (!defined $field_descrs{$param_name}) {
2303 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2304 "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2305 if ($unused_parameters ne "") {
2306 $unused_parameters .= ", ".$param_name;
2308 $unused_parameters = $param_name;
2311 delete $field_descrs{$param_name};
2314 if($param_desc ne "") {
2315 $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";
2320 foreach my $param_name (keys %field_descrs) {
2321 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2322 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2323 if ($missing_parameters ne "") {
2324 $missing_parameters .= ", ".$param_name;
2326 $missing_parameters = $param_name;
2330 # Signals have an implicit user_data parameter which we describe.
2331 if ($symbol_type eq "SIGNAL") {
2332 $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";
2335 # Start a table if we need one.
2336 if ($params_desc ne "") {
2338 <refsect3 role="parameters">\n<title>Parameters</title>
2339 <informaltable role="parameters_table" pgwide="1" frame="none">
2341 <colspec colname="parameters_name" colwidth="150px"/>
2342 <colspec colname="parameters_description"/>
2343 <colspec colname="parameters_annotations" colwidth="200px"/>
2346 $output .= $params_desc;
2347 $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2350 # Output the returns info last
2351 if ($returns ne "") {
2353 <refsect3 role=\"returns\">\n<title>Returns</title>
2355 $output .= $returns;
2356 $output .= "\n</refsect3>";
2359 # remember missing/unused parameters (needed in tmpl-free build)
2360 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2361 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2363 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2364 $AllUnusedSymbols{$symbol}=$unused_parameters;
2367 if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2368 if (! exists ($AllIncompleteSymbols{$symbol})) {
2369 $AllIncompleteSymbols{$symbol}="<parameters>";
2377 #############################################################################
2378 # Function : ParseStabilityLevel
2379 # Description : Parses a stability level and outputs a warning if it isn't
2381 # Arguments : $stability - the stability text.
2382 # $file, $line - context for error message
2383 # $message - description of where the level is from, to use in
2384 # any error message.
2385 # Returns : The parsed stability level string.
2386 #############################################################################
2388 sub ParseStabilityLevel {
2389 my ($stability, $file, $line, $message) = @_;
2391 $stability =~ s/^\s*//;
2392 $stability =~ s/\s*$//;
2393 if ($stability =~ m/^stable$/i) {
2394 $stability = "Stable";
2395 } elsif ($stability =~ m/^unstable$/i) {
2396 $stability = "Unstable";
2397 } elsif ($stability =~ m/^private$/i) {
2398 $stability = "Private";
2400 &LogWarning ($file, $line, "$message is $stability.".
2401 "It should be one of these: Stable, Unstable, or Private.");
2407 #############################################################################
2408 # Function : OutputSGMLFile
2409 # Description : Outputs the final DocBook file for one section.
2410 # Arguments : $file - the name of the file.
2411 # $title - the title from the $MODULE-sections.txt file, which
2412 # will be overridden by the title in the template file.
2413 # $section_id - the SGML id to use for the toplevel tag.
2414 # $includes - comma-separates list of include files added at top of
2415 # synopsis, with '<' '>' around them (if not already enclosed in "").
2416 # $functions_synop - reference to the DocBook for the Functions Synopsis part.
2417 # $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2418 # $functions_details - reference to the DocBook for the Functions Details part.
2419 # $other_details - reference to the DocBook for the Types and Values Details part.
2420 # $signal_synop - reference to the DocBook for the Signal Synopsis part
2421 # $signal_desc - reference to the DocBook for the Signal Description part
2422 # $args_synop - reference to the DocBook for the Arg Synopsis part
2423 # $args_desc - reference to the DocBook for the Arg Description part
2424 # $hierarchy - reference to the DocBook for the Object Hierarchy part
2425 # $interfaces - reference to the DocBook for the Interfaces part
2426 # $implementations - reference to the DocBook for the Known Implementations part
2427 # $prerequisites - reference to the DocBook for the Prerequisites part
2428 # $derived - reference to the DocBook for the Derived Interfaces part
2429 # $file_objects - reference to an array of objects in this file
2430 #############################################################################
2432 sub OutputSGMLFile {
2433 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) = @_;
2435 @TRACE@("Output sgml for file $file with title '$title'\n");
2437 # The edited title overrides the one from the sections file.
2438 my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2439 if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2440 $title = $new_title;
2441 @TRACE@("Found title: $title\n");
2443 my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2444 if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2447 # Don't use ConvertMarkDown here for now since we don't want blocks
2448 $short_desc = &ExpandAbbreviations("$title:Short_description",
2450 @TRACE@("Found short_desc: $short_desc");
2452 my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2453 if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2456 $long_desc = &ConvertMarkDown("$title:Long_description",
2458 @TRACE@("Found long_desc: $long_desc");
2460 my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2461 if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2464 $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2465 @TRACE@("Found see_also: $see_also");
2468 $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2470 my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2471 if (!defined ($stability) || $stability =~ m/^\s*$/) {
2474 $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2475 @TRACE@("Found stability: $stability");
2478 $AnnotationsUsed{$stability} = 1;
2479 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2480 } elsif ($DEFAULT_STABILITY) {
2481 $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2482 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2485 my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2486 if (!defined ($image) || $image =~ m/^\s*$/) {
2494 if ($image =~ /jpe?g$/i) {
2495 $format = "format='JPEG'";
2496 } elsif ($image =~ /png$/i) {
2497 $format = "format='PNG'";
2498 } elsif ($image =~ /svg$/i) {
2499 $format = "format='SVG'";
2504 $image = " <inlinegraphic fileref='$image' $format/>\n"
2507 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2509 my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2512 my $include_output = "";
2514 $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2516 foreach $include (split (/,/, $includes)) {
2517 if ($include =~ m/^\".+\"$/) {
2518 $include_output .= "#include ${include}\n";
2521 $include =~ s/^\s+|\s+$//gs;
2522 $include_output .= "#include <${include}>\n";
2525 $include_output .= "</synopsis></refsect1>\n";
2528 my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2530 my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT";
2531 my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new";
2533 open (OUTPUT, ">$new_sgml_file")
2534 || die "Can't create $new_sgml_file: $!";
2536 my $object_anchors = "";
2537 foreach my $object (@$file_objects) {
2538 next if ($object eq $section_id);
2539 my $id = CreateValidSGMLID($object);
2540 @TRACE@("Adding anchor for $object\n");
2541 $object_anchors .= "<anchor id=\"$id\"$empty_element_end";
2544 # We used to output this, but is messes up our UpdateFileIfChanged code
2545 # since it changes every day (and it is only used in the man pages):
2546 # "<refentry id="$section_id" revision="$mday $month $year">"
2548 if ($OUTPUT_FORMAT eq "xml") {
2549 print OUTPUT $doctype_header;
2553 <refentry id="$section_id">
2555 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2556 <manvolnum>3</manvolnum>
2559 $image</refmiscinfo>
2562 <refname>$title</refname>
2563 <refpurpose>$short_desc</refpurpose>
2566 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2568 <refsect1 id="$section_id.description" role="desc">
2569 <title role="desc.title">Description</title>
2570 $extralinks$long_desc
2572 <refsect1 id="$section_id.functions_details" role="details">
2573 <title role="details.title">Functions</title>
2576 <refsect1 id="$section_id.other_details" role="details">
2577 <title role="details.title">Types and Values</title>
2580 $$args_desc$$signals_desc$see_also
2585 return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2589 #############################################################################
2590 # Function : OutputExtraFile
2591 # Description : Copies an "extra" DocBook file into the output directory,
2592 # expanding abbreviations
2593 # Arguments : $file - the source file.
2594 #############################################################################
2595 sub OutputExtraFile {
2600 ($basename = $file) =~ s!^.*/!!;
2602 my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename";
2603 my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new";
2607 open(EXTRA_FILE, "<$file") || die "Can't open $file";
2611 $contents = <EXTRA_FILE>;
2614 open (OUTPUT, ">$new_sgml_file")
2615 || die "Can't create $new_sgml_file: $!";
2617 print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2620 return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2622 #############################################################################
2623 # Function : OutputBook
2624 # Description : Outputs the SGML entities that need to be included into the
2625 # main SGML file for the module.
2626 # Arguments : $book_top - the declarations of the entities, which are added
2627 # at the top of the main SGML file.
2628 # $book_bottom - the references to the entities, which are
2629 # added in the main SGML file at the desired position.
2630 #############################################################################
2633 my ($book_top, $book_bottom) = @_;
2635 my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top";
2636 my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new";
2638 open (OUTPUT, ">$new_file")
2639 || die "Can't create $new_file: $!";
2640 print OUTPUT $book_top;
2643 &UpdateFileIfChanged ($old_file, $new_file, 0);
2646 $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom";
2647 $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new";
2649 open (OUTPUT, ">$new_file")
2650 || die "Can't create $new_file: $!";
2651 print OUTPUT $book_bottom;
2654 &UpdateFileIfChanged ($old_file, $new_file, 0);
2657 # If the main SGML/XML file hasn't been created yet, we create it here.
2658 # The user can tweak it later.
2659 if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2660 open (OUTPUT, ">$MAIN_SGML_FILE")
2661 || die "Can't create $MAIN_SGML_FILE: $!";
2663 if ($OUTPUT_FORMAT eq "xml") {
2665 <?xml version="1.0"?>
2666 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2667 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2669 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
2675 <!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
2684 <title>$MODULE Reference Manual</title>
2686 for $MODULE [VERSION].
2687 The latest version of this documentation can be found on-line at
2688 <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2693 <title>[Insert title here]</title>
2697 if (-e $OBJECT_TREE_FILE) {
2699 <chapter id="object-tree">
2700 <title>Object Hierarchy</title>
2701 <xi:include href="xml/tree_index.sgml"/>
2707 <index id="api-index-full">
2708 <title>API Index</title>
2709 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2711 <index id="deprecated-api-index" role="deprecated">
2712 <title>Index of deprecated API</title>
2713 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2716 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2725 #############################################################################
2726 # Function : CreateValidSGML
2727 # Description : This turns any chars which are used in SGML into entities,
2728 # e.g. '<' into '<'
2729 # Arguments : $text - the text to turn into proper SGML.
2730 #############################################################################
2732 sub CreateValidSGML {
2734 $text =~ s/&/&/g; # Do this first, or the others get messed up.
2735 $text =~ s/</</g;
2736 $text =~ s/>/>/g;
2737 # browers render single tabs inconsistently
2738 $text =~ s/([^\s])\t([^\s])/$1 $2/g;
2742 #############################################################################
2743 # Function : ConvertSGMLChars
2744 # Description : This is used for text in source code comment blocks, to turn
2745 # chars which are used in SGML into entities, e.g. '<' into
2746 # '<'. Depending on $INLINE_MARKUP_MODE, this is done
2747 # unconditionally or only if the character doesn't seem to be
2748 # part of an SGML construct (tag or entity reference).
2749 # Arguments : $text - the text to turn into proper SGML.
2750 #############################################################################
2752 sub ConvertSGMLChars {
2753 my ($symbol, $text) = @_;
2755 if ($INLINE_MARKUP_MODE) {
2756 # For the XML/SGML mode only convert to entities outside CDATA sections.
2757 return &ModifyXMLElements ($text, $symbol,
2758 "<!\\[CDATA\\[|<programlisting[^>]*>",
2759 \&ConvertSGMLCharsEndTag,
2760 \&ConvertSGMLCharsCallback);
2762 # For the simple non-sgml mode, convert to entities everywhere.
2764 # First, convert freestanding & to &
2765 $text =~ s/&(?![a-zA-Z#]+;)/&/g;
2766 $text =~ s/</</g;
2767 # Allow ">" at beginning of string for blockquote markdown
2768 $text =~ s/(?<=[^\w\n"'\/-])>/>/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 # function level annotations don't end with a colon ':'
2836 if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
2841 @annotations = split(/\)\s*\(/,$1);
2842 foreach $annotation (@annotations) {
2843 # need to search for the longest key-match in %AnnotationDefinition
2845 my $match_annotation="";
2847 foreach $annotationdef (keys %AnnotationDefinition) {
2848 if ($annotation =~ m/^$annotationdef/) {
2849 if (length($annotationdef)>$match_length) {
2850 $match_length=length($annotationdef);
2851 $match_annotation=$annotationdef;
2855 my $annotation_extra = "";
2856 if ($match_annotation ne "") {
2857 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2858 $annotation_extra = " $1";
2860 $AnnotationsUsed{$match_annotation} = 1;
2861 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2864 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2865 "unknown annotation \"$annotation\" in documentation for $symbol.");
2866 $param_annotations .= "[$annotation]";
2870 $param_desc =~ m/^(.*?)\.*\s*$/s;
2871 $param_desc = "$1. ";
2873 if ($param_annotations ne "") {
2874 $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2876 return ($param_desc, $param_annotations);
2879 #############################################################################
2880 # Function : ExpandAbbreviations
2881 # Description : This turns the abbreviations function(), macro(), @param,
2882 # %constant, and #symbol into appropriate DocBook markup.
2883 # CDATA sections and <programlisting> parts are skipped.
2884 # Arguments : $symbol - the symbol being documented, for error messages.
2885 # $text - the text to expand.
2886 #############################################################################
2888 sub ExpandAbbreviations {
2889 my ($symbol, $text) = @_;
2891 # Note: This is a fallback and normally done in the markdown parser
2893 # Convert "|[" and "]|" into the start and end of program listing examples.
2894 # Support \[<!-- language="C" --> modifiers
2895 $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2896 $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2897 $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2899 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2901 return &ModifyXMLElements ($text, $symbol,
2902 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2903 \&ExpandAbbreviationsEndTag,
2904 \&ExpandAbbreviationsCallback);
2908 # Returns the end tag (as a regexp) corresponding to the given start tag.
2909 sub ExpandAbbreviationsEndTag {
2910 my ($start_tag) = @_;
2912 if ($start_tag eq "<!\[CDATA\[") {
2914 } elsif ($start_tag eq "<!DOCTYPE") {
2916 } elsif ($start_tag =~ m/<(\w+)/) {
2921 # Called inside or outside each CDATA or <programlisting> section.
2922 sub ExpandAbbreviationsCallback {
2923 my ($text, $symbol, $tag) = @_;
2925 if ($tag =~ m/^<programlisting/) {
2926 # Handle any embedded CDATA sections.
2927 return &ModifyXMLElements ($text, $symbol,
2929 \&ExpandAbbreviationsEndTag,
2930 \&ExpandAbbreviationsCallback2);
2931 } elsif ($tag eq "") {
2932 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2934 # We are outside any CDATA or <programlisting> sections, so we expand
2935 # any gtk-doc abbreviations.
2937 # Convert '@param()'
2938 # FIXME: we could make those also links ($symbol.$2), but that would be less
2939 # useful as the link target is a few lines up or down
2940 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2942 # Convert 'function()' or 'macro()'.
2943 # if there is abc_*_def() we don't want to make a link to _def()
2944 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2945 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2946 # handle #Object.func()
2947 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2949 # Convert '@param', but not '\@param'.
2950 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2951 $text =~ s/\\\@/\@/g;
2953 # Convert '%constant', but not '\%constant'.
2954 # Also allow negative numbers, e.g. %-1.
2955 $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2956 $text =~ s/\\\%/\%/g;
2958 # Convert '#symbol', but not '\#symbol'.
2959 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2966 # This is called inside a <programlisting>
2967 sub ExpandAbbreviationsCallback2 {
2968 my ($text, $symbol, $tag) = @_;
2971 # We are inside a <programlisting> but outside any CDATA sections,
2972 # so we expand any gtk-doc abbreviations.
2973 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2974 # why not just call it
2975 $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2976 } elsif ($tag eq "<![CDATA[") {
2977 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2978 $text = &ReplaceEntities ($text, $symbol);
2985 my ($symbol, $tag) = @_;;
2988 # Check for things like '#include', '#define', and skip them.
2989 if ($PreProcessorDirectives{$symbol}) {
2993 # Get rid of special suffixes ('-struct','-enum').
2994 $text =~ s/-struct$//;
2995 $text =~ s/-enum$//;
2997 # If the symbol is in the form "Object::signal", then change the symbol to
2998 # "Object-signal" and use "signal" as the text.
2999 if ($symbol =~ s/::/-/) {
3003 # If the symbol is in the form "Object:property", then change the symbol to
3004 # "Object--property" and use "property" as the text.
3005 if ($symbol =~ s/:/--/) {
3010 $text = tagify ($text, $tag);
3013 return &MakeXRef($symbol, $text);
3017 #############################################################################
3018 # Function : ModifyXMLElements
3019 # Description : Looks for given XML element tags within the text, and calls
3020 # the callback on pieces of text inside & outside those elements.
3021 # Used for special handling of text inside things like CDATA
3022 # and <programlisting>.
3023 # Arguments : $text - the text.
3024 # $symbol - the symbol currently being documented (only used for
3026 # $start_tag_regexp - the regular expression to match start tags.
3027 # e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3028 # CDATA sections or programlisting elements.
3029 # $end_tag_func - function which is passed the matched start tag
3030 # and should return the appropriate end tag string regexp.
3031 # $callback - callback called with each part of the text. It is
3032 # called with a piece of text, the symbol being
3033 # documented, and the matched start tag or "" if the text
3034 # is outside the XML elements being matched.
3035 #############################################################################
3036 sub ModifyXMLElements {
3037 my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3038 my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3041 while ($text =~ m/$start_tag_regexp/s) {
3042 $before_tag = $`; # Prematch for last successful match string
3043 $start_tag = $&; # Last successful match
3044 $text = $'; # Postmatch for last successful match string
3046 $result .= &$callback ($before_tag, $symbol, "");
3047 $result .= $start_tag;
3049 # get the matching end-tag for current tag
3050 $end_tag_regexp = &$end_tag_func ($start_tag);
3052 if ($text =~ m/$end_tag_regexp/s) {
3057 $result .= &$callback ($before_tag, $symbol, $start_tag);
3058 $result .= $end_tag;
3060 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3061 "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3062 # Just assume it is all inside the tag.
3063 $result .= &$callback ($text, $symbol, $start_tag);
3068 # Handle any remaining text outside the tags.
3069 $result .= &$callback ($text, $symbol, "");
3078 # Adds a tag around some text.
3079 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3081 my ($text, $elem) = @_;
3082 return "<" . $elem . ">" . $text . "</" . $elem . ">";
3086 #############################################################################
3087 # Function : MakeXRef
3088 # Description : This returns a cross-reference link to the given symbol.
3089 # Though it doesn't try to do this for a few standard C types
3090 # that it knows won't be in the documentation.
3091 # Arguments : $symbol - the symbol to try to create a XRef to.
3092 # $text - text text to put inside the XRef, defaults to $symbol
3093 #############################################################################
3096 my ($symbol, $text) = ($_[0], $_[1]);
3098 $symbol =~ s/^\s+//;
3099 $symbol =~ s/\s+$//;
3101 if (!defined($text)) {
3104 # Get rid of special suffixes ('-struct','-enum').
3105 $text =~ s/-struct$//;
3106 $text =~ s/-enum$//;
3109 if ($symbol =~ m/ /) {
3113 @TRACE@("Getting type link for $symbol -> $text\n");
3115 my $symbol_id = &CreateValidSGMLID ($symbol);
3116 return "<link linkend=\"$symbol_id\">$text</link>";
3120 #############################################################################
3121 # Function : MakeIndexterms
3122 # Description : This returns a indexterm elements for the given symbol
3123 # Arguments : $symbol - the symbol to create indexterms for
3124 #############################################################################
3126 sub MakeIndexterms {
3127 my ($symbol, $id) = @_;
3131 # make the index useful, by ommiting the namespace when sorting
3132 if ($NAME_SPACE ne "") {
3133 if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3134 $sortas=" sortas=\"$1\"";
3138 if (exists $Deprecated{$symbol}) {
3139 $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3140 $IndexEntriesDeprecated{$symbol}=$id;
3141 $IndexEntriesFull{$symbol}=$id;
3143 if (exists $Since{$symbol}) {
3144 my $since = $Since{$symbol};
3148 $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3150 $IndexEntriesSince{$symbol}=$id;
3151 $IndexEntriesFull{$symbol}=$id;
3154 $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3155 $IndexEntriesFull{$symbol}=$id;
3161 #############################################################################
3162 # Function : MakeDeprecationNote
3163 # Description : This returns a deprecation warning for the given symbol.
3164 # Arguments : $symbol - the symbol to try to create a warning for.
3165 #############################################################################
3167 sub MakeDeprecationNote {
3168 my ($symbol) = $_[0];
3170 if (exists $Deprecated{$symbol}) {
3173 $desc .= "<warning><para><literal>$symbol</literal> ";
3175 $note = $Deprecated{$symbol};
3177 if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3178 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3180 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3182 $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3186 $note = &ConvertMarkDown($symbol, $note);
3187 $desc .= " " . $note;
3189 $desc .= "</warning>\n";
3194 #############################################################################
3195 # Function : MakeConditionDescription
3196 # Description : This returns a sumary of conditions for the given symbol.
3197 # Arguments : $symbol - the symbol to try to create the sumary.
3198 #############################################################################
3200 sub MakeConditionDescription {
3201 my ($symbol) = $_[0];
3204 if (exists $Deprecated{$symbol}) {
3209 if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3210 $desc .= "deprecated:$1";
3212 $desc .= "deprecated";
3216 if (exists $Since{$symbol}) {
3221 if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3222 $desc .= "since:$1";
3228 if (exists $StabilityLevel{$symbol}) {
3232 $desc .= "stability:".$StabilityLevel{$symbol};
3237 $cond =~ s/\"/"/g;
3238 $desc=" condition=\"".$cond."\"";
3239 @TRACE@("condition for '$symbol' = '$desc'\n");
3244 #############################################################################
3245 # Function : GetHierarchy
3246 # Description : Returns the DocBook output describing the ancestors and
3247 # immediate children of a GObject subclass. It uses the
3248 # global @Objects and @ObjectLevels arrays to walk the tree.
3250 # Arguments : $object - the GtkObject subclass.
3251 # @hierarchy - previous hierarchy
3252 #############################################################################
3255 my ($object,$hierarchy_ref) = @_;
3256 my @hierarchy = @{$hierarchy_ref};
3258 # Find object in the objects array.
3264 for ($i = 0; $i < @Objects; $i++) {
3266 if ($ObjectLevels[$i] <= $level) {
3269 elsif ($ObjectLevels[$i] == $level + 1) {
3270 push (@children, $Objects[$i]);
3273 elsif ($Objects[$i] eq $object) {
3276 $level = $ObjectLevels[$i];
3283 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3285 push (@ancestors, $object);
3286 @TRACE@("Level: $level\n");
3287 while ($level > 1) {
3289 if ($ObjectLevels[$j] < $level) {
3290 push (@ancestors, $Objects[$j]);
3291 $level = $ObjectLevels[$j];
3292 @TRACE@("Level: $level\n");
3296 # Output the ancestors, indented and with links.
3299 for ($i = $#ancestors; $i >= 0; $i--) {
3301 # Don't add a link to the current object, i.e. when i == 0.
3303 my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]);
3304 $link_text = "<link linkend=\"$ancestor_id\">$ancestors[$i]</link>";
3306 $link_text = "$ancestors[$i]";
3308 my $indented_text = ' ' x ($level * 4) . $link_text;
3309 # Check if we already have this object
3311 for ($j = 0; $j <= $#hierarchy; $j++) {
3312 if ($hierarchy[$j] eq $indented_text) {
3318 # We have a new entry, find insert position in alphabetical order
3319 my $indent = ' ' x ($level * 4);
3321 for ($j = $last_index; $j <= $#hierarchy; $j++) {
3322 if ($hierarchy[$j] !~ m/^${indent}/) {
3326 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3327 my $stripped_text = $hierarchy[$j];
3328 if ($indented_text !~ m/<link linkend/) {
3329 $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3330 $stripped_text =~ s%</link>%%;
3332 if ($indented_text lt $stripped_text) {
3340 $last_index = 1 + $#hierarchy;
3342 splice @hierarchy, $last_index, 0, ($indented_text);
3345 # Already have this one, remmeber index as base insert point
3346 $last_index = $index + 1;
3350 # Output the children, indented and with links.
3351 for ($i = 0; $i <= $#children; $i++) {
3352 my $id = &CreateValidSGMLID ($children[$i]);
3353 my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3354 splice @hierarchy, $last_index, 0, ($indented_text);
3361 #############################################################################
3362 # Function : GetInterfaces
3363 # Description : Returns the DocBook output describing the interfaces
3364 # implemented by a class. It uses the global %Interfaces hash.
3365 # Arguments : $object - the GtkObject subclass.
3366 #############################################################################
3373 # Find object in the objects array.
3374 if (exists($Interfaces{$object})) {
3375 my @ifaces = split(' ', $Interfaces{$object});
3380 for ($i = 0; $i <= $#ifaces; $i++) {
3381 my $id = &CreateValidSGMLID ($ifaces[$i]);
3382 $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3383 if ($i < $#ifaces - 1) {
3386 elsif ($i < $#ifaces) {
3401 #############################################################################
3402 # Function : GetImplementations
3403 # Description : Returns the DocBook output describing the implementations
3404 # of an interface. It uses the global %Interfaces hash.
3405 # Arguments : $object - the GtkObject subclass.
3406 #############################################################################
3408 sub GetImplementations {
3413 foreach my $key (keys %Interfaces) {
3414 if ($Interfaces{$key} =~ /\b$object\b/) {
3415 push (@impls, $key);
3419 @impls = sort @impls;
3422 $object is implemented by
3424 for ($i = 0; $i <= $#impls; $i++) {
3425 my $id = &CreateValidSGMLID ($impls[$i]);
3426 $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3427 if ($i < $#impls - 1) {
3430 elsif ($i < $#impls) {
3445 #############################################################################
3446 # Function : GetPrerequisites
3447 # Description : Returns the DocBook output describing the prerequisites
3448 # of an interface. It uses the global %Prerequisites hash.
3449 # Arguments : $iface - the interface.
3450 #############################################################################
3452 sub GetPrerequisites {
3457 if (exists($Prerequisites{$iface})) {
3462 my @prereqs = split(' ', $Prerequisites{$iface});
3463 for ($i = 0; $i <= $#prereqs; $i++) {
3464 my $id = &CreateValidSGMLID ($prereqs[$i]);
3465 $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3466 if ($i < $#prereqs - 1) {
3469 elsif ($i < $#prereqs) {
3483 #############################################################################
3484 # Function : GetDerived
3485 # Description : Returns the DocBook output describing the derived interfaces
3486 # of an interface. It uses the global %Prerequisites hash.
3487 # Arguments : $iface - the interface.
3488 #############################################################################
3496 foreach my $key (keys %Prerequisites) {
3497 if ($Prerequisites{$key} =~ /\b$iface\b/) {
3498 push (@derived, $key);
3501 if ($#derived >= 0) {
3502 @derived = sort @derived;
3505 $iface is required by
3507 for ($i = 0; $i <= $#derived; $i++) {
3508 my $id = &CreateValidSGMLID ($derived[$i]);
3509 $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3510 if ($i < $#derived - 1) {
3513 elsif ($i < $#derived) {
3528 #############################################################################
3529 # Function : GetSignals
3530 # Description : Returns the synopsis and detailed description DocBook output
3531 # for the signal handlers of a given GtkObject subclass.
3532 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3533 #############################################################################
3541 for ($i = 0; $i <= $#SignalObjects; $i++) {
3542 if ($SignalObjects[$i] eq $object) {
3543 @TRACE@("Found signal: $SignalNames[$i]\n");
3544 my $name = $SignalNames[$i];
3545 my $symbol = "${object}::${name}";
3546 my $id = &CreateValidSGMLID ("$object-$name");
3548 $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3549 $desc .= MakeIndexterms($symbol, $id);
3551 $desc .= OutputSymbolExtraLinks($symbol);
3553 $desc .= "<programlisting language=\"C\">";
3555 $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3556 my $type_modifier = defined($1) ? $1 : "";
3559 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3561 my $ret_type_output = "$type_modifier$xref$pointer";
3562 my $callback_name = "user_function";
3563 $desc .= "${ret_type_output}\n${callback_name} (";
3565 my $indentation = ' ' x (length($callback_name) + 2);
3566 my $pad = $indentation;
3568 my $sourceparams = $SourceSymbolParams{$symbol};
3569 my @params = split ("\n", $SignalPrototypes[$i]);
3572 my $type_len = length("gpointer");
3573 my $name_len = length("user_data");
3574 # do two passes, the first one is to calculate padding
3575 for ($l = 0; $l < 2; $l++) {
3576 for ($j = 0; $j <= $#params; $j++) {
3578 # allow alphanumerics, '_', '[' & ']' in param names
3579 if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3582 if (defined($sourceparams)) {
3583 $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3588 if (!defined($param_name)) {
3589 $param_name = "arg$j";
3592 if (length($type) + length($pointer) > $type_len) {
3593 $type_len = length($type) + length($pointer);
3595 if (length($param_name) > $name_len) {
3596 $name_len = length($param_name);
3600 $xref = &MakeXRef ($type, &tagify($type, "type"));
3601 $pad = ' ' x ($type_len - length($type) - length($pointer));
3602 $desc .= "$xref$pad $pointer${param_name},\n";
3603 $desc .= $indentation;
3606 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3607 "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3611 $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3612 $pad = ' ' x ($type_len - length("gpointer"));
3613 $desc .= "$xref$pad user_data)";
3614 $desc .= "</programlisting>\n";
3616 my $flags = $SignalFlags[$i];
3617 my $flags_string = "";
3619 if (defined ($flags)) {
3620 if ($flags =~ m/f/) {
3621 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3623 elsif ($flags =~ m/l/) {
3624 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3626 elsif ($flags =~ m/c/) {
3627 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3628 $flags_string = "Cleanup";
3630 if ($flags =~ m/r/) {
3631 if ($flags_string) { $flags_string .= " / "; }
3632 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3634 if ($flags =~ m/d/) {
3635 if ($flags_string) { $flags_string .= " / "; }
3636 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3638 if ($flags =~ m/a/) {
3639 if ($flags_string) { $flags_string .= " / "; }
3640 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3642 if ($flags =~ m/h/) {
3643 if ($flags_string) { $flags_string .= " / "; }
3644 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3648 $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";
3650 my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3652 $AllSymbols{$symbol} = 1;
3653 if (defined ($SymbolDocs{$symbol})) {
3654 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3656 $desc .= $symbol_docs;
3658 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3659 $AllDocumentedSymbols{$symbol} = 1;
3662 $desc .= &MakeDeprecationNote($symbol);
3664 $desc .= $parameters;
3665 if ($flags_string) {
3666 $desc .= "<para>Flags: $flags_string</para>\n";
3668 $desc .= OutputSymbolTraits ($symbol);
3669 $desc .= "</refsect2>";
3672 return ($synop, $desc);
3676 #############################################################################
3677 # Function : GetArgs
3678 # Description : Returns the synopsis and detailed description DocBook output
3679 # for the Args of a given GtkObject subclass.
3680 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3681 #############################################################################
3687 my $child_synop = "";
3688 my $child_desc = "";
3689 my $style_synop = "";
3690 my $style_desc = "";
3693 for ($i = 0; $i <= $#ArgObjects; $i++) {
3694 if ($ArgObjects[$i] eq $object) {
3695 @TRACE@("Found arg: $ArgNames[$i]\n");
3696 my $name = $ArgNames[$i];
3697 my $flags = $ArgFlags[$i];
3698 my $flags_string = "";
3702 if ($flags =~ m/c/) {
3703 $kind = "child property";
3706 elsif ($flags =~ m/s/) {
3707 $kind = "style property";
3714 # Remember only one colon so we don't clash with signals.
3715 my $symbol = "${object}:${name}";
3716 # use two dashes and ev. an extra separator here for the same reason.
3717 my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3719 my $type = $ArgTypes[$i];
3721 my $range = $ArgRanges[$i];
3722 my $range_output = CreateValidSGML ($range);
3723 my $default = $ArgDefaults[$i];
3724 my $default_output = CreateValidSGML ($default);
3726 if ($type eq "GtkString") {
3727 $type = "char *";
3729 if ($type eq "GtkSignal") {
3730 $type = "GtkSignalFunc, gpointer";
3731 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3732 . &MakeXRef ("gpointer");
3733 } elsif ($type =~ m/^(\w+)\*$/) {
3734 $type_output = &MakeXRef ($1, &tagify($1, "type")) . " *";
3736 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3739 if ($flags =~ m/r/) {
3740 $flags_string = "Read";
3742 if ($flags =~ m/w/) {
3743 if ($flags_string) { $flags_string .= " / "; }
3744 $flags_string .= "Write";
3746 if ($flags =~ m/x/) {
3747 if ($flags_string) { $flags_string .= " / "; }
3748 $flags_string .= "Construct";
3750 if ($flags =~ m/X/) {
3751 if ($flags_string) { $flags_string .= " / "; }
3752 $flags_string .= "Construct Only";
3755 $AllSymbols{$symbol} = 1;
3757 if (defined($SymbolDocs{$symbol}) &&
3758 !IsEmptyDoc($SymbolDocs{$symbol})) {
3759 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3760 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3761 $AllDocumentedSymbols{$symbol} = 1;
3764 if (!($ArgBlurbs[$i] eq "")) {
3765 $AllDocumentedSymbols{$symbol} = 1;
3767 # FIXME: print a warning?
3768 @TRACE@(".. no description\n");
3770 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3773 my $pad1 = " " x (24 - length ($name));
3775 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";
3776 my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3777 $arg_desc .= MakeIndexterms($symbol, $id);
3779 $arg_desc .= OutputSymbolExtraLinks($symbol);
3781 $arg_desc .= "<programlisting> “$name”$pad1 $type_output</programlisting>\n";
3782 $arg_desc .= $blurb;
3783 $arg_desc .= &MakeDeprecationNote($symbol);
3785 if ($flags_string) {
3786 $arg_desc .= "<para>Flags: $flags_string</para>\n";
3789 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3791 if ($default ne "") {
3792 $arg_desc .= "<para>Default value: $default_output</para>\n";
3794 $arg_desc .= OutputSymbolTraits ($symbol);
3795 $arg_desc .= "</refsect2>\n";
3797 if ($flags =~ m/c/) {
3798 $child_synop .= $arg_synop;
3799 $child_desc .= $arg_desc;
3801 elsif ($flags =~ m/s/) {
3802 $style_synop .= $arg_synop;
3803 $style_desc .= $arg_desc;
3806 $synop .= $arg_synop;
3811 return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3815 #############################################################################
3816 # Function : ReadSourceDocumentation
3817 # Description : This reads in the documentation embedded in comment blocks
3818 # in the source code (for Gnome).
3820 # Parameter descriptions override any in the template files.
3821 # Function descriptions are placed before any description from
3822 # the template files.
3824 # It recursively descends the source directory looking for .c
3825 # files and scans them looking for specially-formatted comment
3828 # Arguments : $source_dir - the directory to scan.
3829 #############m###############################################################
3831 sub ReadSourceDocumentation {
3832 my ($source_dir) = @_;
3833 my ($file, $dir, @suffix_list, $suffix);
3835 # prepend entries from @SOURCE_DIR
3836 for my $dir (@SOURCE_DIRS) {
3837 # Check if the filename is in the ignore list.
3838 if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3839 @TRACE@("Skipping source directory: $source_dir");
3842 @TRACE@("No match for: ".($1 || $source_dir));
3846 @TRACE@("Scanning source directory: $source_dir");
3848 # This array holds any subdirectories found.
3851 @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3853 opendir (SRCDIR, $source_dir)
3854 || die "Can't open source directory $source_dir: $!";
3856 foreach $file (readdir (SRCDIR)) {
3857 if ($file =~ /^\./) {
3859 } elsif (-d "$source_dir/$file") {
3860 push (@subdirs, $file);
3861 } elsif (@suffix_list) {
3862 foreach $suffix (@suffix_list) {
3863 if ($file =~ m/\.\Q${suffix}\E$/) {
3864 &ScanSourceFile ("$source_dir/$file");
3867 } elsif ($file =~ m/\.[ch]$/) {
3868 &ScanSourceFile ("$source_dir/$file");
3873 # Now recursively scan the subdirectories.
3874 foreach $dir (@subdirs) {
3875 &ReadSourceDocumentation ("$source_dir/$dir");
3880 #############################################################################
3881 # Function : ScanSourceFile
3882 # Description : Scans one source file looking for specially-formatted comment
3883 # blocks. Later &MergeSourceDocumentation is used to merge any
3884 # documentation found with the documentation already read in
3885 # from the template files.
3887 # Arguments : $file - the file to scan.
3888 #############################################################################
3890 sub ScanSourceFile {
3894 # prepend entries from @SOURCE_DIR
3895 for my $dir (@SOURCE_DIRS) {
3896 # Check if the filename is in the ignore list.
3897 if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3898 @TRACE@("Skipping source file: $file");
3903 if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3906 &LogWarning ($file, 1, "Can't find basename for this filename.");
3910 # Check if the basename is in the list of files to ignore.
3911 if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3912 @TRACE@("Skipping source file: $file");
3916 @TRACE@("Scanning source file: $file");
3918 open (SRCFILE, $file)
3919 || die "Can't open $file: $!";
3920 my $in_comment_block = 0;
3923 my ($description, $return_desc);
3924 my ($since_desc, $stability_desc, $deprecated_desc);
3928 # Look for the start of a comment block.
3929 if (!$in_comment_block) {
3930 if (m%^\s*/\*.*\*/%) {
3931 #one-line comment - not gtkdoc
3932 } elsif (m%^\s*/\*\*\s%) {
3933 @TRACE@("Found comment block start\n");
3935 $in_comment_block = 1;
3937 # Reset all the symbol data.
3943 $deprecated_desc = "";
3944 $stability_desc = "";
3945 $current_param = -1;
3951 # We're in a comment block. Check if we've found the end of it.
3954 # maybe its not even meant to be a gtk-doc comment?
3955 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3957 # Add the return value description onto the end of the params.
3959 # TODO(ensonic): check for duplicated Return docs
3960 # &LogWarning ($file, $., "Multiple Returns for $symbol.");
3961 push (@params, "Returns");
3962 push (@params, $return_desc);
3964 # Convert special SGML characters
3965 $description = &ConvertSGMLChars ($symbol, $description);
3967 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3968 $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3971 # Handle Section docs
3972 if ($symbol =~ m/SECTION:\s*(.*)/) {
3976 if (scalar %KnownSymbols) {
3977 if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
3978 &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
3982 @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
3983 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3984 @TRACE@(" '".$params[$k]."'\n");
3985 $params[$k] = "\L$params[$k]";
3987 if ($params[$k] eq "short_description") {
3988 $key = "$TMPL_DIR/$real_symbol:Short_Description";
3989 } elsif ($params[$k] eq "see_also") {
3990 $key = "$TMPL_DIR/$real_symbol:See_Also";
3991 } elsif ($params[$k] eq "title") {
3992 $key = "$TMPL_DIR/$real_symbol:Title";
3993 } elsif ($params[$k] eq "stability") {
3994 $key = "$TMPL_DIR/$real_symbol:Stability_Level";
3995 } elsif ($params[$k] eq "section_id") {
3996 $key = "$TMPL_DIR/$real_symbol:Section_Id";
3997 } elsif ($params[$k] eq "include") {
3998 $key = "$TMPL_DIR/$real_symbol:Include";
3999 } elsif ($params[$k] eq "image") {
4000 $key = "$TMPL_DIR/$real_symbol:Image";
4002 if (defined($key)) {
4003 $SourceSymbolDocs{$key}=$params[$k+1];
4004 $SourceSymbolSourceFile{$key} = $file;
4005 $SourceSymbolSourceLine{$key} = $.;
4008 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4009 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4010 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4011 #$SourceSymbolTypes{$symbol} = "SECTION";
4013 @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4014 $SourceSymbolDocs{$symbol} = $description;
4015 $SourceSymbolParams{$symbol} = [ @params ];
4016 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4017 #if (defined $DeclarationTypes{$symbol}) {
4018 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4020 $SourceSymbolSourceFile{$symbol} = $file;
4021 $SourceSymbolSourceLine{$symbol} = $.;
4025 ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4026 $since_desc =~ s/^\s+//;
4027 $since_desc =~ s/\s+$//;
4028 @TRACE@("Since($symbol) : [$since_desc]\n");
4029 $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4030 if(scalar @extra_lines) {
4031 &LogWarning ($file, $., "multi-line since docs found");
4035 if ($stability_desc) {
4036 $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4037 $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4040 if ($deprecated_desc) {
4041 if (!exists $Deprecated{$symbol}) {
4042 # don't warn for signals and properties
4043 #if ($symbol !~ m/::?(.*)/) {
4044 if (defined $DeclarationTypes{$symbol}) {
4045 &LogWarning ($file, $.,
4046 "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4047 " (See the --deprecated-guards option for gtkdoc-scan.)");
4050 $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4054 $in_comment_block = 0;
4058 # Get rid of ' * ' at start of every line in the comment block.
4060 # But make sure we don't get rid of the newline at the end.
4064 @TRACE@("scanning :$_");
4066 # If we haven't found the symbol name yet, look for it.
4068 if (m%^\s*(SECTION:\s*\S+)%) {
4070 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4071 } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._ ]+\)\s*)*$%) {
4073 my $annotation = $2;
4074 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4075 if (defined($annotation)) {
4077 if ($annotation ne "") {
4078 $SymbolAnnotations{$symbol} = $annotation;
4079 @TRACE@("remaining text for $symbol: '$annotation'\n");
4086 if ($in_part eq "description") {
4087 # Get rid of 'Description:'
4088 s%^\s*Description:%%;
4091 if (m%^\s*(returns|return\s+value):%i) {
4092 # we're in param section and have not seen the blank line
4093 if($in_part ne "") {
4095 $in_part = "return";
4098 } elsif (m%^\s*since:%i) {
4099 # we're in param section and have not seen the blank line
4100 if($in_part ne "") {
4105 } elsif (m%^\s*deprecated:%i) {
4106 # we're in param section and have not seen the blank line
4107 if($in_part ne "") {
4108 $deprecated_desc = $';
4109 $in_part = "deprecated";
4112 } elsif (m%^\s*stability:%i) {
4113 $stability_desc = $';
4114 $in_part = "stability";
4118 if ($in_part eq "description") {
4121 } elsif ($in_part eq "return") {
4124 } elsif ($in_part eq "since") {
4127 } elsif ($in_part eq "stability") {
4128 $stability_desc .= $_;
4130 } elsif ($in_part eq "deprecated") {
4131 $deprecated_desc .= $_;
4135 # We must be in the parameters. Check for the empty line below them.
4137 $in_part = "description";
4141 # Look for a parameter name.
4142 if (m%^\s*@(\S+)\s*:\s*%) {
4143 my $param_name = $1;
4144 my $param_desc = $';
4146 @TRACE@("Found parameter: $param_name\n");
4147 # Allow varargs variations
4148 if ($param_name =~ m/^\.\.\.$/) {
4149 $param_name = "...";
4151 @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4153 push (@params, $param_name);
4154 push (@params, $param_desc);
4155 $current_param += $PARAM_FIELD_COUNT;
4158 } elsif ($in_part eq "") {
4159 @TRACE@("continuation for $symbol annotation '$_'");
4160 my $annotation = $_;
4161 $annotation =~ s/^\s+|\s+$//g ;
4162 $SymbolAnnotations{$symbol} .= $annotation;
4165 # We must be in the middle of a parameter description, so add it on
4166 # to the last element in @params.
4167 if ($current_param == -1) {
4168 &LogWarning ($file, $., "Parsing comment block file : parameter expected.");
4170 $params[$#params] .= $_;
4176 #############################################################################
4177 # Function : OutputMissingDocumentation
4178 # Description : Outputs report of documentation coverage to a file
4181 #############################################################################
4183 sub OutputMissingDocumentation {
4184 my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4185 my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4187 my $n_documented = 0;
4188 my $n_incomplete = 0;
4194 my $buffer_deprecated = "";
4195 my $buffer_descriptions = "";
4197 open(UNDOCUMENTED, ">$new_undocumented_file")
4198 || die "Can't create $new_undocumented_file";
4200 foreach $symbol (sort (keys (%AllSymbols))) {
4201 # FIXME: should we print LogWarnings for undocumented stuff?
4203 #my $ssfile = &GetSymbolSourceFile($symbol);
4204 #my $ssline = &GetSymbolSourceLine($symbol);
4205 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4207 if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4209 if (exists ($AllDocumentedSymbols{$symbol})) {
4211 if (exists ($AllIncompleteSymbols{$symbol})) {
4213 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4214 #$buffer .= "\t0: ".$location;
4216 } elsif (exists $Deprecated{$symbol}) {
4217 if (exists ($AllIncompleteSymbols{$symbol})) {
4219 $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4220 #$buffer .= "\t1a: ".$location;
4222 $buffer_deprecated .= $symbol . "\n";
4223 #$buffer .= "\t1b: ".$location;
4226 if (exists ($AllIncompleteSymbols{$symbol})) {
4228 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4229 #$buffer .= "\t2a: ".$location;
4231 $buffer .= $symbol . "\n";
4232 #$buffer .= "\t2b: ".$location;
4235 } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4237 if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4238 || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4241 # cut off the leading namespace ($TMPL_DIR)
4242 $symbol =~ m/^.*\/(.*)$/;
4243 $buffer_descriptions .= $1 . "\n";
4248 $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
4253 $percent = ($n_documented / $total) * 100.0;
4256 printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4257 print UNDOCUMENTED "$n_documented symbols documented.\n";
4258 print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4259 print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
4261 print UNDOCUMENTED $buffer;
4262 close (UNDOCUMENTED);
4264 return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4266 printf "%.0f%% symbol docs coverage", $percent;
4267 print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4268 print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4272 #############################################################################
4273 # Function : OutputUndeclaredSymbols
4274 # Description : Outputs symbols that are listed in the section file, but not
4275 # declaration is found in the sources
4278 #############################################################################
4280 sub OutputUndeclaredSymbols {
4281 my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4282 my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4284 open(UNDECLARED, ">$new_undeclared_file")
4285 || die "Can't create $new_undeclared_file";
4287 if (%UndeclaredSymbols) {
4288 print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4289 print UNDECLARED "\n";
4290 print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4294 return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4297 #############################################################################
4298 # Function : OutputUnusedSymbols
4299 # Description : Outputs symbols that are documented in comments, but not
4300 # declared in the sources
4303 #############################################################################
4305 sub OutputUnusedSymbols {
4307 my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4308 my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4310 open (UNUSED, ">$new_unused_file")
4311 || die "Can't open $new_unused_file";
4313 foreach $symbol (sort keys (%Declarations)) {
4314 if (!defined ($DeclarationOutput{$symbol})) {
4315 print (UNUSED "$symbol\n");
4319 foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4320 print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4324 if ($num_unused != 0) {
4325 &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4326 "They should be added to $MODULE-sections.txt in the appropriate place.");
4329 return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4333 #############################################################################
4334 # Function : OutputAllSymbols
4335 # Description : Outputs list of all symbols to a file
4338 #############################################################################
4340 sub OutputAllSymbols {
4341 my $n_documented = 0;
4347 open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4348 || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4350 foreach $symbol (sort (keys (%AllSymbols))) {
4351 print SYMBOLS $symbol . "\n";
4357 #############################################################################
4358 # Function : OutputSymbolsWithoutSince
4359 # Description : Outputs list of all symbols without a since tag to a file
4362 #############################################################################
4364 sub OutputSymbolsWithoutSince {
4365 my $n_documented = 0;
4371 open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4372 || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4374 foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4375 if (!defined $Since{$symbol}) {
4376 print SYMBOLS $symbol . "\n";
4384 #############################################################################
4385 # Function : MergeSourceDocumentation
4386 # Description : This merges documentation read from a source file into the
4387 # documentation read in from a template file.
4389 # Parameter descriptions override any in the template files.
4390 # Function descriptions are placed before any description from
4391 # the template files.
4394 #############################################################################
4396 sub MergeSourceDocumentation {
4400 if (scalar %SymbolDocs) {
4401 @Symbols=keys (%SymbolDocs);
4402 @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4405 # filter scanned declarations, with what we suppress from -sections.txt
4407 foreach $symbol (keys (%Declarations)) {
4408 if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4412 # , add the rest from -sections.txt
4413 foreach $symbol (keys (%KnownSymbols)) {
4414 if ($KnownSymbols{$symbol} == 1) {
4418 # and add whats found in the source
4419 foreach $symbol (keys (%SourceSymbolDocs)) {
4422 @Symbols = keys (%tmp);
4423 @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4425 foreach $symbol (@Symbols) {
4426 $AllSymbols{$symbol} = 1;
4428 my $have_tmpl_docs = 0;
4430 ## see if the symbol is documented in template
4431 my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4432 my $check_tmpl_doc =$tmpl_doc;
4433 # remove all xml-tags and whitespaces
4434 $check_tmpl_doc =~ s/<.*?>//g;
4435 $check_tmpl_doc =~ s/\s//g;
4437 if ($check_tmpl_doc ne "") {
4438 $have_tmpl_docs = 1;
4440 # if the docs have just an empty para, don't merge that.
4441 $check_tmpl_doc = $tmpl_doc;
4442 $check_tmpl_doc =~ s/(\s|\n)//msg;
4443 if ($check_tmpl_doc eq "<para></para>") {
4448 if (exists ($SourceSymbolDocs{$symbol})) {
4449 my $type = $DeclarationTypes {$symbol};
4451 @TRACE@("merging [$symbol] from source\n");
4453 my $item = "Parameter";
4454 if (defined ($type)) {
4455 if ($type eq 'STRUCT') {
4457 } elsif ($type eq 'ENUM') {
4459 } elsif ($type eq 'UNION') {
4466 my $src_doc = $SourceSymbolDocs{$symbol};
4467 # remove leading and training whitespaces
4468 $src_doc =~ s/^\s+//;
4469 $src_doc =~ s/\s+$//;
4471 # Don't output warnings for overridden titles as titles are
4472 # automatically generated in the -sections.txt file, and thus they
4473 # are often overridden.
4474 if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4475 # check if content is different
4476 if ($tmpl_doc ne $src_doc) {
4477 #print "[$tmpl_doc] [$src_doc]\n";
4478 &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4479 "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4483 if ($src_doc ne "") {
4484 $AllDocumentedSymbols{$symbol} = 1;
4487 # Do not add <para> to nothing, it breaks missing docs checks.
4488 my $src_doc_para = "";
4489 if ($src_doc ne "") {
4490 $src_doc_para = $src_doc;
4493 if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4494 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4495 } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4496 # For the title/summary/see also section docs we don't want to
4497 # add any <para> tags.
4498 $SymbolDocs{$symbol} = "$src_doc"
4500 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4504 if ($symbol =~ m/.*::.*/) {
4505 # For signals we prefer the param names from the source docs,
4506 # since the ones from the templates are likely to contain the
4507 # artificial argn names which are generated by gtkdoc-scangobj.
4508 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4509 # FIXME: we need to check for empty docs here as well!
4511 # The templates contain the definitive parameter names and order,
4512 # so we will not change that. We only override the actual text.
4513 my $tmpl_params = $SymbolParams{$symbol};
4514 if (!defined ($tmpl_params)) {
4515 @TRACE@("No merge needed for $symbol\n");
4516 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4517 # FIXME: we still like to get the number of params and merge
4518 # 1) we would noticed that params have been removed/renamed
4519 # 2) we would catch undocumented params
4520 # params are not (yet) exported in -decl.txt so that we
4521 # could easily grab them :/
4523 my $params = $SourceSymbolParams{$symbol};
4525 @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4526 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4527 my $tmpl_param_name = $$tmpl_params[$j];
4529 # Try to find the param in the source comment documentation.
4532 @TRACE@(" try merge param $tmpl_param_name\n");
4533 for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4534 my $param_name = $$params[$k];
4535 my $param_desc = $$params[$k + 1];
4537 @TRACE@(" test param $param_name\n");
4538 # We accept changes in case, since the Gnome source
4539 # docs contain a lot of these.
4540 if ("\L$param_name" eq "\L$tmpl_param_name") {
4543 # Override the description.
4544 $$tmpl_params[$j + 1] = $param_desc;
4546 # Set the name to "" to mark it as used.
4552 # If it looks like the parameters are there, but not
4553 # in the right place, try to explain a bit better.
4554 if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4555 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4556 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4560 # Now we output a warning if parameters have been described which
4562 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4563 my $param_name = $$params[$j];
4565 # the template builder cannot detect if a macro returns
4567 if(($type eq "MACRO") && ($param_name eq "Returns")) {
4568 # FIXME: do we need to add it then to tmpl_params[] ?
4569 my $num=$#$tmpl_params;
4570 @TRACE@(" adding Returns: to macro docs for $symbol.\n");
4571 $$tmpl_params[$num+1]="Returns";
4572 $$tmpl_params[$num+2]=$$params[$j+1];
4575 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4576 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4582 if ($have_tmpl_docs) {
4583 $AllDocumentedSymbols{$symbol} = 1;
4584 @TRACE@("merging [$symbol] from template\n");
4587 @TRACE@("[$symbol] undocumented\n");
4591 # if this symbol is documented, check if docs are complete
4592 $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4593 # remove all xml-tags and whitespaces
4594 $check_tmpl_doc =~ s/<.*?>//g;
4595 $check_tmpl_doc =~ s/\s//g;
4596 if ($check_tmpl_doc ne "") {
4597 my $tmpl_params = $SymbolParams{$symbol};
4598 if (defined ($tmpl_params)) {
4599 my $type = $DeclarationTypes {$symbol};
4601 my $item = "Parameter";
4602 if (defined ($type)) {
4603 if ($type eq 'STRUCT') {
4605 } elsif ($type eq 'ENUM') {
4607 } elsif ($type eq 'UNION') {
4614 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4616 if ($#$tmpl_params > 0) {
4618 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4619 # Output a warning if the parameter is empty and
4620 # remember for stats.
4621 my $tmpl_param_name = $$tmpl_params[$j];
4622 my $tmpl_param_desc = $$tmpl_params[$j + 1];
4623 if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4624 if (exists ($AllIncompleteSymbols{$symbol})) {
4625 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4627 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4629 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4630 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4635 if ($#$tmpl_params == 0) {
4636 $AllIncompleteSymbols{$symbol}="<items>";
4637 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4638 "$item descriptions for $symbol are missing in source code comment block.");
4640 # $#$tmpl_params==-1 means we don't know about parameters
4641 # this unfortunately does not tell if there should be some
4646 @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4649 #############################################################################
4650 # Function : IsEmptyDoc
4651 # Description : Check if a doc-string is empty. Its also regarded as empty if
4652 # it only consist of whitespace or e.g. FIXME.
4653 # Arguments : the doc-string
4654 #############################################################################
4658 if ($doc =~ /^\s*$/) {
4662 if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4669 #############################################################################
4670 # Function : ConvertMarkDown
4671 # Description : Converts mark down syntax to the respective docbook.
4672 # http://de.wikipedia.org/wiki/Markdown
4673 # Inspired by the design of ParseDown
4674 # http://parsedown.org/
4675 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4676 # Arguments : the symbol name, the doc-string
4677 #############################################################################
4679 sub ConvertMarkDown {
4680 my ($symbol, $text) = @_;
4682 $text = &MarkDownParse ($text, $symbol);
4687 # SUPPORTED MARKDOWN
4688 # ==================
4697 # Setext-style Headers
4698 # --------------------
4706 # Ordered (unnested) Lists
4707 # ------------------------
4711 # 1. item 2 with loooong
4716 # Note: we require a blank line above the list items
4719 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4721 sub MarkDownParseBlocks {
4722 my ($linesref, $symbol, $context) = @_;
4725 my $md_block = { type => "" };
4727 OUTER: foreach $line (@$linesref) {
4728 my $first_char = substr ($line, 0, 1);
4729 my $deindented_line;
4731 if ($md_block->{"type"} eq "markup") {
4732 if (!$md_block->{"closed"}) {
4733 if (index ($line, $md_block->{"start"}) != -1) {
4734 $md_block->{"depth"}++;
4736 if (index ($line, $md_block->{"end"}) != -1) {
4737 if ($md_block->{"depth"} > 0) {
4738 $md_block->{"depth"}--;
4740 $md_block->{"closed"} = 1;
4743 $md_block->{"text"} .= "\n" . $line;
4748 $deindented_line = $line;
4749 $deindented_line =~ s/^\s+//;
4751 if ($md_block->{"type"} eq "heading") {
4752 # a heading is ended by any level less than or equal
4753 if ($md_block->{"level"} == 1) {
4754 if ($line =~ /^={4,}[ \t]*$/) {
4755 my $text = pop @{$md_block->{"lines"}};
4756 $md_block->{"interrupted"} = 0;
4757 push @md_blocks, $md_block;
4759 $md_block = { type => "heading",
4764 } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4765 $md_block->{"interrupted"} = 0;
4766 push @md_blocks, $md_block;
4768 $md_block = { type => "heading",
4775 # push lines into the block until the end is reached
4776 push @{$md_block->{"lines"}}, $line;
4780 if ($line =~ /^[=]{4,}[ \t]*$/) {
4781 my $text = pop @{$md_block->{"lines"}};
4782 $md_block->{"interrupted"} = 0;
4783 push @md_blocks, $md_block;
4785 $md_block = { type => "heading",
4790 } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4791 my $text = pop @{$md_block->{"lines"}};
4792 $md_block->{"interrupted"} = 0;
4793 push @md_blocks, $md_block;
4795 $md_block = { type => "heading",
4800 } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4801 $md_block->{"interrupted"} = 0;
4802 push @md_blocks, $md_block;
4804 $md_block = { type => "heading",
4808 level => length($1) };
4811 # push lines into the block until the end is reached
4812 push @{$md_block->{"lines"}}, $line;
4816 } elsif ($md_block->{"type"} eq "code") {
4817 if ($line =~ /^[ \t]*\]\|/) {
4818 push @md_blocks, $md_block;
4819 $md_block = { type => "paragraph",
4823 push @{$md_block->{"lines"}}, $line;
4828 if ($deindented_line eq "") {
4829 $md_block->{"interrupted"} = 1;
4833 if ($md_block->{"type"} eq "quote") {
4834 if (!$md_block->{"interrupted"}) {
4835 $line =~ s/^[ ]*>[ ]?//;
4836 push @{$md_block->{"lines"}}, $line;
4839 } elsif ($md_block->{"type"} eq "li") {
4840 my $marker = $md_block->{"marker"};
4841 if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4842 my $indentation = $1;
4843 if ($md_block->{"indentation"} ne $indentation) {
4844 push @{$md_block->{"lines"}}, $line;
4847 my $ordered = $md_block->{"ordered"};
4848 $lines =~ s/^[ ]{0,4}//;
4849 $md_block->{"last"} = 0;
4850 push @md_blocks, $md_block;
4851 $md_block = { type => "li",
4852 ordered => $ordered,
4853 indentation => $indentation,
4857 lines => [ $lines ] };
4862 if ($md_block->{"interrupted"}) {
4863 if ($first_char eq " ") {
4864 push @{$md_block->{"lines"}}, "";
4865 $line =~ s/^[ ]{0,4}//;
4866 push @{$md_block->{"lines"}}, $line;
4867 $md_block->{"interrupted"} = 0;
4871 $line =~ s/^[ ]{0,4}//;
4872 push @{$md_block->{"lines"}}, $line;
4877 # indentation sensitive types
4879 if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4881 push @md_blocks, $md_block;
4883 $md_block = { type => "heading",
4887 level => length($1) };
4890 } elsif ($line =~ /^={4,}[ \t]*$/) {
4891 # setext heading (====)
4893 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4894 push @md_blocks, $md_block;
4895 $md_block->{"type"} = "heading";
4896 $md_block->{"lines"} = [];
4897 $md_block->{"level"} = 1;
4901 } elsif ($line =~ /^-{4,}[ \t]*$/) {
4902 # setext heading (-----)
4904 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4905 push @md_blocks, $md_block;
4906 $md_block->{"type"} = "heading";
4907 $md_block->{"lines"} = [];
4908 $md_block->{"level"} = 2;
4912 } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4914 $md_block->{"interrupted"} = 1;
4915 push @md_blocks, $md_block;
4916 $md_block = { type => "code",
4922 # indentation insensitive types
4923 if ($line =~ /^[ ]*<!DOCTYPE/) {
4924 push @md_blocks, $md_block;
4926 $md_block = { type => "markup",
4927 text => $deindented_line,
4933 } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4934 # markup, including <?xml version="1.0"?>
4936 my $is_self_closing = defined($2);
4937 # FIXME: why do we need to skip https? here, if we generalize this to all
4938 # uri schemes we get parsing errors
4939 if (! $MD_TEXT_LEVEL_ELEMENTS{$tag} && $tag !~ /^https?/) {
4940 push @md_blocks, $md_block;
4942 if ($is_self_closing) {
4943 $md_block = { type => "self-closing tag",
4944 text => $deindented_line };
4945 $is_self_closing = 0;
4949 $md_block = { type => "markup",
4950 text => $deindented_line,
4951 start => "<" . $tag . ">",
4952 end => "</" . $tag . ">",
4955 if ($deindented_line =~ /<\/$tag>/) {
4956 $md_block->{"closed"} = 1;
4960 } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
4962 push @md_blocks, $md_block;
4964 my $indentation = $1;
4965 $lines =~ s/^[ ]{0,4}//;
4966 $md_block = { type => "li",
4968 indentation => $indentation,
4972 lines => [ $lines ] };
4974 } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
4975 push @md_blocks, $md_block;
4976 $md_block = { type => "quote",
4983 if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
4984 push @md_blocks, $md_block;
4986 my $indentation = $1;
4987 $lines =~ s/^[ ]{0,4}//;
4989 $md_block = { type => "li",
4991 indentation => $indentation,
4992 marker => "\\d+[.]",
4995 lines => [ $lines ] };
5001 if ($md_block->{"type"} eq "paragraph") {
5002 if ($md_block->{"interrupted"}) {
5003 push @md_blocks, $md_block;
5004 $md_block = { type => "paragraph",
5008 $md_block->{"text"} .= "\n" . $line;
5011 push @md_blocks, $md_block;
5012 $md_block = { type => "paragraph",
5017 push @md_blocks, $md_block;
5024 sub MarkDownParseSpanElementsInner {
5025 my ($text, $markersref) = @_;
5027 my %markers = map { $_ => 1 } @$markersref;
5029 while ($text ne "") {
5030 my $closest_marker = "";
5031 my $closest_marker_index = 0;
5032 my $closest_marker_position = -1;
5033 my $text_marker = "";
5040 while ( ($marker, $use) = each %markers ) {
5041 my $marker_position;
5047 $marker_position = index ($text, $marker);
5049 if ($marker_position < 0) {
5050 $markers{$marker} = 0;
5054 if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5055 $closest_marker = $marker;
5056 $closest_marker_index = $i;
5057 $closest_marker_position = $marker_position;
5061 if ($closest_marker_position >= 0) {
5062 $text_marker = substr ($text, $closest_marker_position);
5065 if ($text_marker eq "") {
5071 $markup .= substr ($text, 0, $closest_marker_position);
5072 $text = substr ($text, $closest_marker_position);
5073 @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5075 if ($closest_marker eq "![" || $closest_marker eq "[") {
5078 if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5081 %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5084 $offset = length ($&);
5085 if ($element{"!"}) {
5089 $remaining_text = substr ($text, $offset);
5090 if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5095 $offset += length ($&);
5096 } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5097 $element{"ref"} = $1;
5098 $offset += length ($&);
5105 if ($element{"»"}) {
5106 $element{"»"} =~ s/&/&/g;
5107 $element{"»"} =~ s/</</g;
5109 if ($element{"!"}) {
5110 $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5112 if (defined ($element{"a"})) {
5113 $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5116 $markup .= "</inlinemediaobject>";
5117 } elsif ($element{"ref"}) {
5118 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5119 $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5121 if (defined ($element{"#"})) {
5122 # title attribute not supported
5125 $markup .= ">" . $element{"a"} . "</link>";
5127 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5128 $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5130 if (defined ($element{"#"})) {
5131 # title attribute not supported
5134 $markup .= ">" . $element{"a"} . "</ulink>";
5137 $markup .= $closest_marker;
5138 if ($closest_marker eq "![") {
5144 } elsif ($closest_marker eq "<") {
5145 if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5146 my $element_url = $1;
5147 $element_url =~ s/&/&/g;
5148 $element_url =~ s/</</g;
5150 $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5151 $offset = length ($&);
5152 } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5153 $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5154 $offset = length ($&);
5155 } elsif ($text =~ /^<[^>]+?>/) {
5157 $offset = length ($&);
5162 } elsif ($closest_marker eq "\\") {
5163 my $special_char = substr ($text, 1, 1);
5164 if ($MD_ESCAPABLE_CHARS{$special_char} ||
5165 $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5166 $markup .= $special_char;
5172 } elsif ($closest_marker eq "`") {
5173 if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5174 my $element_text = $2;
5175 $markup .= "<literal>" . $element_text . "</literal>";
5176 $offset = length ($&);
5181 } elsif ($closest_marker eq "@") {
5182 # Convert '@param()'
5183 # FIXME: we could make those also links ($symbol.$2), but that would be less
5184 # useful as the link target is a few lines up or down
5185 if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5186 $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5187 $offset = length ($&);
5188 } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5189 # Convert '@param', but not '\@param'.
5190 $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5191 $offset = length ($&);
5192 } elsif ($text =~ /^\\\@/) {
5194 $offset = length ($&);
5199 } elsif ($closest_marker eq "#") {
5200 if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5201 # handle #Object.func()
5202 $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5203 $offset = length ($&);
5204 } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5205 # Convert '#symbol', but not '\#symbol'.
5206 $markup .= $1 . &MakeHashXRef ($2, "type");
5207 $offset = length ($&);
5208 } elsif ($text =~ /^\\#/) {
5210 $offset = length ($&);
5215 } elsif ($closest_marker eq "%") {
5216 if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5217 # Convert '%constant', but not '\%constant'.
5218 # Also allow negative numbers, e.g. %-1.
5219 $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5220 $offset = length ($&);
5221 } elsif ($text =~ /^\\%/) {
5223 $offset = length ($&);
5231 $text = substr ($text, $offset);
5238 sub MarkDownParseSpanElements {
5240 my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5242 $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5244 # Convert 'function()' or 'macro()'.
5245 # if there is abc_*_def() we don't want to make a link to _def()
5246 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5247 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5252 sub ReplaceEntities {
5253 my ($text, $symbol) = @_;
5255 my @entities = ( [ "<", "<" ],
5264 [ "&", "&" ] ); # Do this last, or the others get messed up.
5267 # Expand entities in <programlisting> even inside CDATA since
5268 # we changed the definition of |[ to add CDATA
5269 for ($i = 0; $i <= $#entities; $i++) {
5270 $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5276 sub MarkDownOutputDocBook {
5277 my ($blocksref, $symbol, $context) = @_;
5280 my @blocks = @$blocksref;
5282 foreach $block (@blocks) {
5286 if ($block->{"type"} eq "paragraph") {
5287 $text = &MarkDownParseSpanElements ($block->{"text"});
5288 if ($context eq "li" && $output eq "") {
5289 if ($block->{"interrupted"}) {
5290 $output .= "\n"."<para>".$text."</para>"."\n";
5292 $output .= "<para>".$text."</para>";
5298 $output .= "<para>".$text."</para>"."\n";
5301 } elsif ($block->{"type"} eq "heading") {
5304 $title = &MarkDownParseSpanElements ($block->{"text"});
5306 if ($block->{"level"} == 1) {
5312 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5313 if (defined ($block->{"id"})) {
5314 $output .= "<" . $tag . " id=\"" . $block->{"id"} . "\">";
5316 $output .= "<" . $tag . ">";
5319 $output .= "<title>" . $title . "</title>" . $text . "</" . $tag . ">\n";
5320 } elsif ($block->{"type"} eq "li") {
5321 my $tag = "itemizedlist";
5323 if ($block->{"first"}) {
5324 if ($block->{"ordered"}) {
5325 $tag = "orderedlist";
5327 $output .= "<".$tag.">\n";
5330 if ($block->{"interrupted"}) {
5331 push @{$block->{"lines"}}, "";
5334 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5335 $output .= "<listitem>".$text."</listitem>\n";
5336 if ($block->{"last"}) {
5337 if ($block->{"ordered"}) {
5338 $tag = "orderedlist";
5340 $output .= "</".$tag.">\n";
5342 } elsif ($block->{"type"} eq "quote") {
5343 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5344 $output .= "<blockquote>\n" . $text . "</blockquote>\n";
5345 } elsif ($block->{"type"} eq "code") {
5346 if ($block->{"language"}) {
5347 $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5349 $output .= "<informalexample><programlisting><![CDATA[\n";
5351 foreach (@{$block->{"lines"}}) {
5352 $output .= &ReplaceEntities ($_, $symbol) . "\n";
5354 $output .= "]]></programlisting></informalexample>\n";
5355 } elsif ($block->{"type"} eq "markup") {
5356 $text = &ExpandAbbreviations($symbol, $block->{"text"});
5357 $output .= $text."\n";
5359 $output .= $block->{"text"}."\n";
5366 sub MarkDownParseLines {
5367 my ($linesref, $symbol, $context) = @_;
5369 my @lines = @$linesref;
5372 @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5373 $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5379 my ($text, $symbol) = @_;
5382 # take out some variability in line endings
5383 $text =~ s%\r\n%\n%g;
5387 @lines = split("\n", $text);
5388 $text = MarkDownParseLines(\@lines, $symbol, "");
5393 #############################################################################
5394 # LIBRARY FUNCTIONS - These functions are used in both gtkdoc-mkdb and
5395 # gtkdoc-mktmpl and should eventually be moved to a
5397 #############################################################################
5399 #############################################################################
5400 # Function : ReadDeclarationsFile
5401 # Description : This reads in a file containing the function/macro/enum etc.
5404 # Note that in some cases there are several declarations with
5405 # the same name, e.g. for conditional macros. In this case we
5406 # set a flag in the %DeclarationConditional hash so the
5407 # declaration is not shown in the docs.
5409 # If a macro and a function have the same name, e.g. for
5410 # gtk_object_ref, the function declaration takes precedence.
5412 # Some opaque structs are just declared with 'typedef struct
5413 # _name name;' in which case the declaration may be empty.
5414 # The structure may have been found later in the header, so
5415 # that overrides the empty declaration.
5417 # Arguments : $file - the declarations file to read
5418 # $override - if declarations in this file should override
5419 # any current declaration.
5420 #############################################################################
5422 sub ReadDeclarationsFile {
5423 my ($file, $override) = @_;
5425 if ($override == 0) {
5427 %DeclarationTypes = ();
5428 %DeclarationConditional = ();
5429 %DeclarationOutput = ();
5433 || die "Can't open $file: $!";
5434 my $declaration_type = "";
5435 my $declaration_name;
5437 my $is_deprecated = 0;
5439 if (!$declaration_type) {
5440 if (m/^<([^>]+)>/) {
5441 $declaration_type = $1;
5442 $declaration_name = "";
5443 @TRACE@("Found declaration: $declaration_type\n");
5447 if (m%^<NAME>(.*)</NAME>%) {
5448 $declaration_name = $1;
5449 } elsif (m%^<DEPRECATED/>%) {
5451 } elsif (m%^</$declaration_type>%) {
5452 @TRACE@("Found end of declaration: $declaration_name\n");
5453 # Check that the declaration has a name
5454 if ($declaration_name eq "") {
5455 &LogWarning ($file, $., "$declaration_type has no name.\n");
5458 # If the declaration is an empty typedef struct _XXX XXX
5459 # set the flag to indicate the struct has a typedef.
5460 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5461 && $declaration =~ m/^\s*$/) {
5462 @TRACE@("Struct has typedef: $declaration_name\n");
5463 $StructHasTypedef{$declaration_name} = 1;
5466 # Check if the symbol is already defined.
5467 if (defined ($Declarations{$declaration_name})
5468 && $override == 0) {
5469 # Function declarations take precedence.
5470 if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5472 } elsif ($declaration_type eq 'FUNCTION') {
5473 if ($is_deprecated) {
5474 $Deprecated{$declaration_name} = "";
5476 $Declarations{$declaration_name} = $declaration;
5477 $DeclarationTypes{$declaration_name} = $declaration_type;
5478 } elsif ($DeclarationTypes{$declaration_name}
5479 eq $declaration_type) {
5480 # If the existing declaration is empty, or is just a
5481 # forward declaration of a struct, override it.
5482 if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5483 if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5484 if ($is_deprecated) {
5485 $Deprecated{$declaration_name} = "";
5487 $Declarations{$declaration_name} = $declaration;
5488 } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5489 # Ignore an empty or forward declaration.
5491 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5494 # set flag in %DeclarationConditional hash for
5495 # multiply defined macros/typedefs.
5496 $DeclarationConditional{$declaration_name} = 1;
5499 &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5502 if ($is_deprecated) {
5503 $Deprecated{$declaration_name} = "";
5505 $Declarations{$declaration_name} = $declaration;
5506 $DeclarationTypes{$declaration_name} = $declaration_type;
5509 $declaration_type = "";
5520 #############################################################################
5521 # Function : ReadSignalsFile
5522 # Description : This reads in an existing file which contains information on
5523 # all GTK signals. It creates the arrays @SignalNames and
5524 # @SignalPrototypes containing info on the signals. The first
5525 # line of the SignalPrototype is the return type of the signal
5526 # handler. The remaining lines are the parameters passed to it.
5527 # The last parameter, "gpointer user_data" is always the same
5528 # so is not included.
5529 # Arguments : $file - the file containing the signal handler prototype
5531 #############################################################################
5533 sub ReadSignalsFile {
5541 my $signal_prototype;
5543 # Reset the signal info.
5544 @SignalObjects = ();
5546 @SignalReturns = ();
5548 @SignalPrototypes = ();
5553 if (!open (INPUT, $file)) {
5554 warn "Can't open $file - skipping signals\n";
5561 $signal_object = "";
5563 $signal_returns = "";
5564 $signal_prototype = "";
5567 if (m/^<NAME>(.*)<\/NAME>/) {
5569 if ($signal_name =~ m/^(.*)::(.*)$/) {
5570 $signal_object = $1;
5571 ($signal_name = $2) =~ s/_/-/g;
5572 @TRACE@("Found signal: $signal_name\n");
5574 &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5576 } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5577 $signal_returns = $1;
5578 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5580 } elsif (m%^</SIGNAL>%) {
5581 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5582 push (@SignalObjects, $signal_object);
5583 push (@SignalNames, $signal_name);
5584 push (@SignalReturns, $signal_returns);
5585 push (@SignalFlags, $signal_flags);
5586 push (@SignalPrototypes, $signal_prototype);
5589 $signal_prototype .= $_;
5597 #############################################################################
5598 # Function : ReadTemplateFile
5599 # Description : This reads in the manually-edited documentation file
5600 # corresponding to the file currently being created, so we can
5601 # insert the documentation at the appropriate places.
5602 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5603 # is a hash of arrays.
5604 # NOTE: This function is duplicated in gtkdoc-mktmpl (but
5605 # slightly different).
5606 # Arguments : $docsfile - the template file to read in.
5607 # $skip_unused_params - 1 if the unused parameters should be
5609 #############################################################################
5611 sub ReadTemplateFile {
5612 my ($docsfile, $skip_unused_params) = @_;
5614 my $template = "$docsfile.sgml";
5615 if (! -f $template) {
5616 @TRACE@("File doesn't exist: $template\n");
5620 # start with empty hashes, we merge the source comment for each file
5626 my $current_type = ""; # Type of symbol being read.
5627 my $current_symbol = ""; # Name of symbol being read.
5628 my $symbol_doc = ""; # Description of symbol being read.
5629 my @params; # Parameter names and descriptions of current
5630 # function/macro/function typedef.
5631 my $current_param = -1; # Index of parameter currently being read.
5632 # Note that the param array contains pairs
5633 # of param name & description.
5634 my $in_unused_params = 0; # True if we are reading in the unused params.
5635 my $in_deprecated = 0;
5637 my $in_stability = 0;
5639 open (DOCS, "$template")
5640 || die "Can't open $template: $!";
5642 @TRACE@("reading template $template");
5645 if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5648 if ($symbol eq "Title"
5649 || $symbol eq "Short_Description"
5650 || $symbol eq "Long_Description"
5651 || $symbol eq "See_Also"
5652 || $symbol eq "Stability_Level"
5653 || $symbol eq "Include"
5654 || $symbol eq "Image") {
5656 $symbol = $docsfile . ":" . $symbol;
5659 @TRACE@("Found symbol: $symbol\n");
5660 # Remember file and line for the symbol
5661 $SymbolSourceFile{$symbol} = $template;
5662 $SymbolSourceLine{$symbol} = $.;
5664 # Store previous symbol, but remove any trailing blank lines.
5665 if ($current_symbol ne "") {
5666 $symbol_doc =~ s/\s+$//;
5667 $SymbolTypes{$current_symbol} = $current_type;
5668 $SymbolDocs{$current_symbol} = $symbol_doc;
5670 # Check that the stability level is valid.
5671 if ($StabilityLevel{$current_symbol}) {
5672 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5675 if ($current_param >= 0) {
5676 $SymbolParams{$current_symbol} = [ @params ];
5678 # Delete any existing params in case we are overriding a
5679 # previously read template.
5680 delete $SymbolParams{$current_symbol};
5683 $current_type = $type;
5684 $current_symbol = $symbol;
5685 $current_param = -1;
5686 $in_unused_params = 0;
5693 } elsif (m/^<!-- # Unused Parameters # -->/) {
5694 @TRACE@("Found unused parameters\n");
5695 $in_unused_params = 1;
5698 } elsif ($in_unused_params && $skip_unused_params) {
5699 # When outputting the DocBook we skip unused parameters.
5700 @TRACE@("Skipping unused param: $_");
5704 # Check if param found. Need to handle "..." and "format...".
5705 if (s/^\@([\w\.]+):\040?//) {
5706 my $param_name = $1;
5707 my $param_desc = $_;
5708 # Allow variations of 'Returns'
5709 if ($param_name =~ m/^[Rr]eturns?$/) {
5710 $param_name = "Returns";
5712 # Allow varargs variations
5713 if ($param_name =~ m/^.*\.\.\.$/) {
5714 $param_name = "...";
5717 # strip trailing whitespaces and blank lines
5720 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5722 if ($param_name eq "Deprecated") {
5724 $Deprecated{$current_symbol} = $_;
5725 } elsif ($param_name eq "Since") {
5728 $Since{$current_symbol} = $_;
5729 } elsif ($param_name eq "Stability") {
5731 $StabilityLevel{$current_symbol} = $_;
5733 push (@params, $param_name);
5734 push (@params, $param_desc);
5735 $current_param += $PARAM_FIELD_COUNT;
5738 # strip trailing whitespaces and blank lines
5743 if ($in_deprecated) {
5744 $Deprecated{$current_symbol} .= $_;
5745 } elsif ($in_since) {
5746 &LogWarning ($template, $., "multi-line since docs found");
5747 #$Since{$current_symbol} .= $_;
5748 } elsif ($in_stability) {
5749 $StabilityLevel{$current_symbol} .= $_;
5750 } elsif ($current_param >= 0) {
5751 $params[$current_param] .= $_;
5760 # Remember to finish the current symbol doccs.
5761 if ($current_symbol ne "") {
5763 $symbol_doc =~ s/\s+$//;
5764 $SymbolTypes{$current_symbol} = $current_type;
5765 $SymbolDocs{$current_symbol} = $symbol_doc;
5767 # Check that the stability level is valid.
5768 if ($StabilityLevel{$current_symbol}) {
5769 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5772 if ($current_param >= 0) {
5773 $SymbolParams{$current_symbol} = [ @params ];
5775 # Delete any existing params in case we are overriding a
5776 # previously read template.
5777 delete $SymbolParams{$current_symbol};
5786 #############################################################################
5787 # Function : ReadObjectHierarchy
5788 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5789 # the GtkObject subclasses described in this module (and their
5791 # It places them in the @Objects array, and places their level
5792 # in the object hierarchy in the @ObjectLevels array, at the
5793 # same index. GtkObject, the root object, has a level of 1.
5795 # This also generates tree_index.sgml as it goes along.
5798 #############################################################################
5800 sub ReadObjectHierarchy {
5804 if (! -f $OBJECT_TREE_FILE) {
5807 if (!open (INPUT, $OBJECT_TREE_FILE)) {
5808 warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5812 # Only emit objects if they are supposed to be documented, or if
5813 # they have documented children. To implement this, we maintain a
5814 # stack of pending objects which will be emitted if a documented
5816 my @pending_objects = ();
5817 my @pending_levels = ();
5823 my $level = (length($`)) / 2 + 1;
5830 while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5831 my $pobject = pop(@pending_objects);
5832 my $plevel = pop(@pending_levels);
5835 push (@pending_objects, $object);
5836 push (@pending_levels, $level);
5838 if (exists($KnownSymbols{$object})) {
5839 while ($#pending_levels >= 0) {
5840 $object = shift @pending_objects;
5841 $level = shift @pending_levels;
5842 $xref = &MakeXRef ($object);
5844 push (@tree, ' ' x ($level * 4) . "$xref");
5845 push (@Objects, $object);
5846 push (@ObjectLevels, $level);
5847 $ObjectRoots{$object} = $root;
5851 # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5857 # FIXME: use $OUTPUT_FORMAT
5858 # my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.$OUTPUT_FORMAT";
5859 my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml";
5860 my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new";
5862 open (OUTPUT, ">$new_tree_index")
5863 || die "Can't create $new_tree_index: $!";
5865 if ($OUTPUT_FORMAT eq "xml") {
5866 my $tree_header = $doctype_header;
5868 $tree_header =~ s/<!DOCTYPE \w+/<!DOCTYPE screen/;
5869 print (OUTPUT "$tree_header");
5871 print (OUTPUT "<screen>\n" . &AddTreeLineArt(\@tree) . "\n</screen>\n");
5874 &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5879 #############################################################################
5880 # Function : ReadInterfaces
5881 # Description : This reads in the $MODULE.interfaces file.
5884 #############################################################################
5886 sub ReadInterfaces {
5889 if (! -f $INTERFACES_FILE) {
5892 if (!open (INPUT, $INTERFACES_FILE)) {
5893 warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5899 my ($object, @ifaces) = split;
5900 if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5901 my @knownIfaces = ();
5903 # filter out private interfaces, but leave foreign interfaces
5904 foreach my $iface (@ifaces) {
5905 if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5906 push (@knownIfaces, $iface);
5910 $Interfaces{$object} = join(' ', @knownIfaces);
5911 @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
5913 @TRACE@("skipping interfaces for unknown symbol: $object\n");
5919 #############################################################################
5920 # Function : ReadPrerequisites
5921 # Description : This reads in the $MODULE.prerequisites file.
5924 #############################################################################
5926 sub ReadPrerequisites {
5927 %Prerequisites = ();
5929 if (! -f $PREREQUISITES_FILE) {
5932 if (!open (INPUT, $PREREQUISITES_FILE)) {
5933 warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
5939 my ($iface, @prereqs) = split;
5940 if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
5941 my @knownPrereqs = ();
5943 # filter out private prerequisites, but leave foreign prerequisites
5944 foreach my $prereq (@prereqs) {
5945 if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
5946 push (@knownPrereqs, $prereq);
5950 $Prerequisites{$iface} = join(' ', @knownPrereqs);
5956 #############################################################################
5957 # Function : ReadArgsFile
5958 # Description : This reads in an existing file which contains information on
5959 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
5960 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
5962 # Arguments : $file - the file containing the arg information.
5963 #############################################################################
5978 # Reset the args info.
5991 if (!open (INPUT, $file)) {
5992 warn "Can't open $file - skipping args\n";
6009 if (m/^<NAME>(.*)<\/NAME>/) {
6011 if ($arg_name =~ m/^(.*)::(.*)$/) {
6013 ($arg_name = $2) =~ s/_/-/g;
6014 @TRACE@("Found arg: $arg_name\n");
6016 &LogWarning ($file, $., "Invalid argument name: $arg_name");
6018 } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6020 } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6022 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6024 } elsif (m/^<NICK>(.*)<\/NICK>/) {
6026 } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6028 if ($arg_blurb eq "(null)") {
6030 &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6032 } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6034 } elsif (m%^</ARG>%) {
6035 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6036 push (@ArgObjects, $arg_object);
6037 push (@ArgNames, $arg_name);
6038 push (@ArgTypes, $arg_type);
6039 push (@ArgRanges, $arg_range);
6040 push (@ArgFlags, $arg_flags);
6041 push (@ArgNicks, $arg_nick);
6042 push (@ArgBlurbs, $arg_blurb);
6043 push (@ArgDefaults, $arg_default);
6051 #############################################################################
6052 # Function : AddTreeLineArt
6053 # Description : Add unicode lineart to a pre-indented string array and returns
6054 # it as as multiline string.
6055 # Arguments : @tree - array of indented strings.
6056 #############################################################################
6058 sub AddTreeLineArt {
6059 my @tree = @{$_[0]};
6064 # iterate bottom up over the tree
6065 for ($i = $#tree; $i >= 0; $i--) {
6066 # count leading spaces
6067 $tree[$i] =~ /^([^<A-Za-z]*)/;
6068 $indent = length( $1 );
6069 # replace with ╰───, if place of ╰ is not space insert ├
6071 if (substr($tree[$i],$indent-4,1) eq " ") {
6072 substr($tree[$i],$indent-4,4) = "--- ";
6074 substr($tree[$i],$indent-4,4) = "+-- ";
6076 # go lines up while space and insert |
6077 for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6078 substr($tree[$j],$indent-4,1) = '|';
6083 my $res = join("\n", @tree);
6084 # unicode chars for: ╰──
6085 $res =~ s%---%<phrase role=\"lineart\">╰──</phrase>%g;
6086 # unicde chars for: ├──
6087 $res =~ s%\+--%<phrase role=\"lineart\">├──</phrase>%g;
6088 # unicode char for: │
6089 $res =~ s%\|%<phrase role=\"lineart\">│</phrase>%g;
6095 #############################################################################
6096 # Function : CheckIsObject
6097 # Description : Returns 1 if the given name is a GObject or a subclass.
6098 # It uses the global @Objects array.
6099 # Note that the @Objects array only contains classes in the
6100 # current module and their ancestors - not all GObject classes.
6101 # Arguments : $name - the name to check.
6102 #############################################################################
6106 my $root = $ObjectRoots{$name};
6107 # Let GBoxed pass as an object here to get -struct appended to the id
6108 # and prevent conflicts with sections.
6109 return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6113 #############################################################################
6114 # Function : MakeReturnField
6115 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6116 # Arguments : $str - the string to pad.
6117 #############################################################################
6119 sub MakeReturnField {
6122 return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6125 #############################################################################
6126 # Function : GetSymbolSourceFile
6127 # Description : Get the filename where the symbol docs where taken from.
6128 # Arguments : $symbol - the symbol name
6129 #############################################################################
6131 sub GetSymbolSourceFile {
6134 if (defined($SourceSymbolSourceFile{$symbol})) {
6135 return $SourceSymbolSourceFile{$symbol};
6136 } elsif (defined($SymbolSourceFile{$symbol})) {
6137 return $SymbolSourceFile{$symbol};
6143 #############################################################################
6144 # Function : GetSymbolSourceLine
6145 # Description : Get the file line where the symbol docs where taken from.
6146 # Arguments : $symbol - the symbol name
6147 #############################################################################
6149 sub GetSymbolSourceLine {
6152 if (defined($SourceSymbolSourceLine{$symbol})) {
6153 return $SourceSymbolSourceLine{$symbol};
6154 } elsif (defined($SymbolSourceLine{$symbol})) {
6155 return $SymbolSourceLine{$symbol};