4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998 Damon Chaplin
6 # 2007,2008,2009 Stefan Kost
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #############################################################################
24 # Script : gtkdoc-mkdb
25 # Description : This creates the DocBook files from the edited templates.
26 #############################################################################
32 push @INC, '@PACKAGE_DATA_DIR@';
33 require "gtkdoc-common.pl";
37 # name of documentation module
42 my $SOURCE_SUFFIXES = "";
43 my $IGNORE_FILES = "";
47 my $EXPAND_CONTENT_FILES = "";
48 my $INLINE_MARKUP_MODE;
49 my $DEFAULT_STABILITY;
53 my $OUTPUT_ALL_SYMBOLS;
54 my $OUTPUT_SYMBOLS_WITHOUT_SINCE;
56 my %optctl = ('module' => \$MODULE,
57 'source-dir' => \@SOURCE_DIRS,
58 'source-suffixes' => \$SOURCE_SUFFIXES,
59 'ignore-files' => \$IGNORE_FILES,
60 'output-dir' => \$SGML_OUTPUT_DIR,
61 'tmpl-dir' => \$TMPL_DIR,
62 'version' => \$PRINT_VERSION,
63 'help' => \$PRINT_HELP,
64 'main-sgml-file' => \$MAIN_SGML_FILE,
65 'expand-content-files' => \$EXPAND_CONTENT_FILES,
66 'sgml-mode' => \$INLINE_MARKUP_MODE,
67 'xml-mode' => \$INLINE_MARKUP_MODE,
68 'default-stability' => \$DEFAULT_STABILITY,
69 'default-includes' => \$DEFAULT_INCLUDES,
70 'output-format' => \$OUTPUT_FORMAT,
71 'name-space' => \$NAME_SPACE,
72 'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
73 'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
75 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
76 "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version", "outputallsymbols",
77 "outputsymbolswithoutsince",
78 "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
79 "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
80 "output-format:s", "name-space:s");
91 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
92 && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
98 gtkdoc-mkdb version @VERSION@ - generate docbook files
100 --module=MODULE_NAME Name of the doc module being parsed
101 --source-dir=DIRNAME Directories which contain inline reference material
102 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
103 --ignore-files=FILES Files or directories which should not be scanned
104 May be used more than once for multiple directories
105 --output-dir=DIRNAME Directory to put the generated DocBook files in
106 --tmpl-dir=DIRNAME Directory in which template files may be found
107 --main-sgml-file=FILE File containing the toplevel DocBook file.
108 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
109 --output-format=FORMAT Format to use for the generated docbook, XML or SGML.
110 --{xml,sgml}-mode Allow DocBook markup in inline documentation.
111 --default-stability=LEVEL Specify default stability Level. Valid values are
112 Stable, Unstable, or Private.
113 --default-includes=FILENAMES Specify default includes for section Synopsis
114 --name-space=NS Omit namespace in index.
115 --version Print the version of this program
116 --help Print this help
121 my ($empty_element_end, $doctype_header);
123 # autodetect output format
124 if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
125 if (!$MAIN_SGML_FILE) {
126 if (-e "${MODULE}-docs.xml") {
127 $OUTPUT_FORMAT = "xml";
129 $OUTPUT_FORMAT = "sgml";
132 if ($MAIN_SGML_FILE =~ m/.*\.(.*ml)$/i) {
133 $OUTPUT_FORMAT = lc($1);
138 $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
141 @TRACE@(" output-format: [$OUTPUT_FORMAT]\n");
143 if ($OUTPUT_FORMAT eq "xml") {
144 if (!$MAIN_SGML_FILE) {
145 # backwards compatibility
146 if (-e "${MODULE}-docs.sgml") {
147 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
149 $MAIN_SGML_FILE = "${MODULE}-docs.xml";
152 $empty_element_end = "/>";
154 if (-e $MAIN_SGML_FILE) {
155 open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
156 $doctype_header = "";
158 if (/^\s*<(book|chapter|article)/) {
159 # check that the top-level tag or the doctype decl contain the xinclude namespace decl
160 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
161 $doctype_header = "";
165 $doctype_header .= $_;
168 $doctype_header =~ s/<!DOCTYPE \w+/<!DOCTYPE refentry/;
169 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
170 # FIXME: not sure if we can do this now, as people already work-around the problem
171 # $doctype_header =~ s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#g;
174 "<?xml version=\"1.0\"?>\n" .
175 "<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.3//EN\"\n" .
176 " \"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd\"\n" .
178 " <!ENTITY % local.common.attrib \"xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'\">\n" .
182 if (!$MAIN_SGML_FILE) {
183 $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
185 $empty_element_end = ">";
186 $doctype_header = "";
191 # All the files are written in subdirectories beneath here.
192 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
194 # This is where we put all the DocBook output.
195 $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/$OUTPUT_FORMAT";
197 # This file contains the object hierarchy.
198 my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
200 # This file contains the interfaces.
201 my $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
203 # This file contains the prerequisites.
204 my $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
206 # This file contains signal arguments and names.
207 my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
209 # The file containing Arg information.
210 my $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
212 # These global arrays store information on signals. Each signal has an entry
213 # in each of these arrays at the same index, like a multi-dimensional array.
214 my @SignalObjects; # The GtkObject which emits the signal.
215 my @SignalNames; # The signal name.
216 my @SignalReturns; # The return type.
217 my @SignalFlags; # Flags for the signal
218 my @SignalPrototypes; # The rest of the prototype of the signal handler.
220 # These global arrays store information on Args. Each Arg has an entry
221 # in each of these arrays at the same index, like a multi-dimensional array.
222 my @ArgObjects; # The GtkObject which has the Arg.
223 my @ArgNames; # The Arg name.
224 my @ArgTypes; # The Arg type - gint, GtkArrowType etc.
225 my @ArgFlags; # How the Arg can be used - readable/writable etc.
226 my @ArgNicks; # The nickname of the Arg.
227 my @ArgBlurbs; # Docstring of the Arg.
228 my @ArgDefaults; # Default value of the Arg.
229 my @ArgRanges; # The range of the Arg type
230 # These global hashes store declaration info keyed on a symbol name.
232 my %DeclarationTypes;
233 my %DeclarationConditional;
234 my %DeclarationOutput;
238 my %StructHasTypedef;
240 # These global hashes store the existing documentation.
244 my %SymbolSourceFile;
245 my %SymbolSourceLine;
247 # These global hashes store documentation scanned from the source files.
248 my %SourceSymbolDocs;
249 my %SourceSymbolParams;
250 my %SourceSymbolSourceFile;
251 my %SourceSymbolSourceLine;
253 # all documentation goes in here, so we can do coverage analysis
255 my %AllIncompleteSymbols;
256 my %AllUnusedSymbols;
257 my %AllDocumentedSymbols;
259 # Undeclared yet documented symbols
260 my %UndeclaredSymbols;
262 # These global arrays store GObject, subclasses and the hierarchy (also of
263 # non-object derived types).
271 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
272 # section they are defined
277 # collects index entries
278 my %IndexEntriesFull;
279 my %IndexEntriesSince;
280 my %IndexEntriesDeprecated;
282 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
283 my %PreProcessorDirectives;
284 $PreProcessorDirectives{"assert"} = 1;
285 $PreProcessorDirectives{"define"} = 1;
286 $PreProcessorDirectives{"elif"} = 1;
287 $PreProcessorDirectives{"else"} = 1;
288 $PreProcessorDirectives{"endif"} = 1;
289 $PreProcessorDirectives{"error"} = 1;
290 $PreProcessorDirectives{"if"} = 1;
291 $PreProcessorDirectives{"ifdef"} = 1;
292 $PreProcessorDirectives{"ifndef"} = 1;
293 $PreProcessorDirectives{"include"} = 1;
294 $PreProcessorDirectives{"line"} = 1;
295 $PreProcessorDirectives{"pragma"} = 1;
296 $PreProcessorDirectives{"unassert"} = 1;
297 $PreProcessorDirectives{"undef"} = 1;
298 $PreProcessorDirectives{"warning"} = 1;
300 # remember used annotation (to write minimal glossary)
303 my %AnnotationDefinition = (
304 # the GObjectIntrospection annotations are defined at:
305 # https://live.gnome.org/GObjectIntrospection/Annotations
306 'allow-none' => "NULL is OK, both for passing and for returning.",
307 'nullable' => "NULL may be passed as the value in, out, in-out; or as a return value.",
308 'optional' => "NULL may be passed instead of a pointer to a location.",
309 'array' => "Parameter points to an array of items.",
310 'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
311 'attributes' => "Free-form key-value pairs.",
312 'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
313 'constructor' => "This symbol is a constructor, not a static method.",
314 'destroy' => "This parameter is a 'destroy_data', for callbacks.",
315 'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
316 'element-type' => "Generics and defining elements of containers and arrays.",
317 'error-domains' => "Typed errors. Similar to throws in Java.",
318 'foreign' => "This is a foreign struct.",
319 'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
320 'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
321 'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
322 'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
323 'method' => "This is a method",
324 'not-error' => "A GError parameter is not to be handled like a normal GError.",
325 'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
326 'out caller-allocates' => "Out parameter, where caller must allocate storage.",
327 'out callee-allocates' => "Out parameter, where caller must allocate storage.",
328 'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
329 'rename-to' => "Rename the original symbol's name to SYMBOL.",
330 'scope call' => "The callback is valid only during the call to the method.",
331 'scope async' => "The callback is valid until first called.",
332 'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
333 'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
334 'skip' => "Exposed in C code, not necessarily available in other languages.",
335 'transfer container' => "Free data container after the code is done.",
336 'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
337 'transfer full' => "Free data after the code is done.",
338 'transfer none' => "Don't free data after the code is done.",
339 'type' => "Override the parsed C type with given type.",
340 'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
341 'virtual' => "This is the invoker for a virtual method.",
342 'value' => "The specified value overrides the evaluated value of the constant.",
343 # Stability Level definition
344 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
346 The intention of a Stable interface is to enable arbitrary third parties to
347 develop applications to these interfaces, release them, and have confidence that
348 they will run on all minor releases of the product (after the one in which the
349 interface was introduced, and within the same major release). Even at a major
350 release, incompatible changes are expected to be rare, and to have strong
354 Unstable interfaces are experimental or transitional. They are typically used to
355 give outside developers early access to new or rapidly changing technology, or
356 to provide an interim solution to a problem where a more general solution is
357 anticipated. No claims are made about either source or binary compatibility from
358 one minor release to the next.
360 The Unstable interface level is a warning that these interfaces are subject to
361 change without warning and should not be used in unbundled products.
363 Given such caveats, customer impact need not be a factor when considering
364 incompatible changes to an Unstable interface in a major or minor release.
365 Nonetheless, when such changes are introduced, the changes should still be
366 mentioned in the release notes for the affected release.
369 An interface that can be used within the GNOME stack itself, but that is not
370 documented for end-users. Such functions should only be used in specified and
375 # Elements to consider non-block items in MarkDown parsing
376 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
388 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
404 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
407 # Create the root DocBook output directory if it doens't exist.
408 if (! -e $SGML_OUTPUT_DIR) {
409 mkdir ("$SGML_OUTPUT_DIR", 0777)
410 || die "Can't create directory: $SGML_OUTPUT_DIR";
413 # Function and other declaration output settings.
414 my $RETURN_TYPE_FIELD_WIDTH = 20;
415 my $SYMBOL_FIELD_WIDTH = 36;
416 my $MAX_SYMBOL_FIELD_WIDTH = 40;
417 my $SIGNAL_FIELD_WIDTH = 16;
418 my $PARAM_FIELD_COUNT = 2;
420 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
421 &ReadSignalsFile ($SIGNALS_FILE);
422 &ReadArgsFile ($ARGS_FILE);
423 &ReadObjectHierarchy;
427 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
428 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
429 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
432 for my $dir (@SOURCE_DIRS) {
433 &ReadSourceDocumentation ($dir);
436 my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt");
438 # If any of the DocBook SGML files have changed, update the timestamp file (so
439 # it can be used for Makefile dependencies).
440 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
442 # try to detect the common prefix
443 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
444 if ($NAME_SPACE eq "") {
451 foreach my $symbol (keys(%IndexEntriesFull)) {
452 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
453 if (length($symbol)>$pos) {
454 $letter=substr($symbol,$pos,1);
455 # stop prefix scanning
456 if ($letter eq "_") {
460 # Should we also stop on a uppercase char, if last was lowercase
461 # GtkWidget, if we have the 'W' and had the 't' before
462 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
463 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
464 # need to recound each time as this is per symbol
465 $prefix{uc($letter)}++;
469 if ($letter ne "" && $letter ne "_") {
472 foreach $letter (keys(%prefix)) {
473 #print "$letter: $prefix{$letter}.\n";
474 if ($prefix{$letter}>$maxsymbols) {
476 $maxsymbols=$prefix{$letter};
479 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
480 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
483 $NAME_SPACE .= $maxletter;
490 } while ($ratio > 0.9);
491 #print "most symbols start with $NAME_SPACE\n";
495 &OutputDeprecatedIndex;
497 &OutputAnnotationGlossary;
499 open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
500 || die "Can't create $ROOT_DIR/sgml.stamp: $!";
501 print (TIMESTAMP "timestamp");
505 #############################################################################
506 # Function : OutputObjectList
507 # Description : This outputs the alphabetical list of objects, in a columned
509 # FIXME: Currently this also outputs ancestor objects
510 # which may not actually be in this module.
512 #############################################################################
514 sub OutputObjectList {
517 # FIXME: use $OUTPUT_FORMAT
518 # my $old_object_index = "$SGML_OUTPUT_DIR/object_index.$OUTPUT_FORMAT";
519 my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml";
520 my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new";
522 open (OUTPUT, ">$new_object_index")
523 || die "Can't create $new_object_index: $!";
525 if ($OUTPUT_FORMAT eq "xml") {
526 my $header = $doctype_header;
528 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE informaltable/;
529 print (OUTPUT "$header");
532 print (OUTPUT <<EOF);
533 <informaltable pgwide="1" frame="none">
534 <tgroup cols="$cols">
535 <colspec colwidth="1*"${empty_element_end}
536 <colspec colwidth="1*"${empty_element_end}
537 <colspec colwidth="1*"${empty_element_end}
543 foreach $object (sort (@Objects)) {
544 my $xref = &MakeXRef ($object);
545 if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
546 print (OUTPUT "<entry>$xref</entry>\n");
547 if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
551 # emit an empty row, since empty tables are invalid
552 print (OUTPUT "<row><entry> </entry></row>\n");
555 if ($count % $cols > 0) {
556 print (OUTPUT "</row>\n");
560 print (OUTPUT <<EOF);
561 </tbody></tgroup></informaltable>
565 &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
568 #############################################################################
569 # Function : TrimTextBlock
570 # Description : Trims extra whitespace. Empty lines inside a block are
572 # Arguments : $desc - the text block to trim. May contain newlines.
573 #############################################################################
578 # strip leading spaces on the block
580 # strip trailing spaces on every line
581 $desc =~ s/\s+$/\n/mg;
587 #############################################################################
588 # Function : OutputSGML
589 # Description : This collects the output for each section of the docs, and
590 # outputs each file when the end of the section is found.
591 # Arguments : $file - the $MODULE-sections.txt file which contains all of
592 # the functions/macros/structs etc. being documented, organised
593 # into sections and subsections.
594 #############################################################################
599 @TRACE@("Reading: $file\n");
601 || die "Can't open $file: $!";
604 my $book_bottom = "";
605 my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
606 my $section_includes = "";
613 my $functions_synop = "";
614 my $other_synop = "";
615 my $functions_details = "";
616 my $other_details = "";
617 my $signals_synop = "";
618 my $signals_desc = "";
620 my $child_args_synop = "";
621 my $style_args_synop = "";
623 my $child_args_desc = "";
624 my $style_args_desc = "";
625 my $hierarchy_str = "";
628 my $implementations = "";
629 my $prerequisites = "";
631 my @file_objects = ();
633 my %symbol_def_line = ();
635 # merge the source docs, in case there are no templates
636 &MergeSourceDocumentation;
642 } elsif (m/^<SECTION>/) {
646 %symbol_def_line = ();
648 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
649 $other_synop .= "\n";
650 $functions_synop .= "\n";
653 } elsif (m/^<SUBSECTION>/) {
655 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
657 @TRACE@("Section: $title\n");
659 # We don't want warnings if object & class structs aren't used.
660 $DeclarationOutput{$title} = 1;
661 $DeclarationOutput{"${title}Class"} = 1;
662 $DeclarationOutput{"${title}Iface"} = 1;
663 $DeclarationOutput{"${title}Interface"} = 1;
665 } elsif (m/^<FILE>(.*)<\/FILE>/) {
667 if (! defined $templates{$filename}) {
668 if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
669 &MergeSourceDocumentation;
670 $templates{$filename}=$.;
673 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
674 "Previous occurrence on line ".$templates{$filename}.".");
676 if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
677 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
678 # Remove trailing blanks
682 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
684 $section_includes = $1;
686 if (defined $DEFAULT_INCLUDES) {
687 &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
694 } elsif (m/^<\/SECTION>/) {
695 @TRACE@("End of section: $title\n");
696 if ($num_symbols > 0) {
698 if ($OUTPUT_FORMAT eq "xml") {
699 $book_bottom .= " <xi:include href=\"xml/$filename.xml\"/>\n";
701 $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n";
702 $book_bottom .= " &$section_id;\n";
705 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
706 if ($section_includes) {
707 &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
709 $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
711 if ($section_includes eq "") {
712 $section_includes = $includes;
715 $signals_synop =~ s/^\n*//g;
716 $signals_synop =~ s/\n+$/\n/g;
717 if ($signals_synop ne '') {
718 $signals_synop = <<EOF;
719 <refsect1 id="$section_id.signals" role="signal_proto">
720 <title role="signal_proto.title">Signals</title>
721 <informaltable frame="none">
723 <colspec colname="signals_return" colwidth="150px"/>
724 <colspec colname="signals_name" colwidth="300px"/>
725 <colspec colname="signals_flags" colwidth="200px"/>
733 $signals_desc = TrimTextBlock($signals_desc);
734 $signals_desc = <<EOF;
735 <refsect1 id="$section_id.signal-details" role="signals">
736 <title role="signals.title">Signal Details</title>
742 $args_synop =~ s/^\n*//g;
743 $args_synop =~ s/\n+$/\n/g;
744 if ($args_synop ne '') {
746 <refsect1 id="$section_id.properties" role="properties">
747 <title role="properties.title">Properties</title>
748 <informaltable frame="none">
750 <colspec colname="properties_type" colwidth="150px"/>
751 <colspec colname="properties_name" colwidth="300px"/>
752 <colspec colname="properties_flags" colwidth="200px"/>
760 $args_desc = TrimTextBlock($args_desc);
762 <refsect1 id="$section_id.property-details" role="property_details">
763 <title role="property_details.title">Property Details</title>
769 $child_args_synop =~ s/^\n*//g;
770 $child_args_synop =~ s/\n+$/\n/g;
771 if ($child_args_synop ne '') {
772 $args_synop .= <<EOF;
773 <refsect1 id="$section_id.child-properties" role="child_properties">
774 <title role="child_properties.title">Child Properties</title>
775 <informaltable frame="none">
777 <colspec colname="child_properties_type" colwidth="150px"/>
778 <colspec colname="child_properties_name" colwidth="300px"/>
779 <colspec colname="child_properties_flags" colwidth="200px"/>
787 $child_args_desc = TrimTextBlock($child_args_desc);
789 <refsect1 id="$section_id.child-property-details" role="child_property_details">
790 <title role="child_property_details.title">Child Property Details</title>
796 $style_args_synop =~ s/^\n*//g;
797 $style_args_synop =~ s/\n+$/\n/g;
798 if ($style_args_synop ne '') {
799 $args_synop .= <<EOF;
800 <refsect1 id="$section_id.style-properties" role="style_properties">
801 <title role="style_properties.title">Style Properties</title>
802 <informaltable frame="none">
804 <colspec colname="style_properties_type" colwidth="150px"/>
805 <colspec colname="style_properties_name" colwidth="300px"/>
806 <colspec colname="style_properties_flags" colwidth="200px"/>
814 $style_args_desc = TrimTextBlock($style_args_desc);
816 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
817 <title role="style_properties_details.title">Style Property Details</title>
823 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
824 if ($hierarchy_str ne "") {
825 $hierarchy_str = <<EOF;
826 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
827 <title role="object_hierarchy.title">Object Hierarchy</title>
828 <screen>$hierarchy_str
834 $interfaces =~ TrimTextBlock($interfaces);
835 if ($interfaces ne "") {
837 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
838 <title role="impl_interfaces.title">Implemented Interfaces</title>
844 $implementations = TrimTextBlock($implementations);
845 if ($implementations ne "") {
846 $implementations = <<EOF;
847 <refsect1 id="$section_id.implementations" role="implementations">
848 <title role="implementations.title">Known Implementations</title>
854 $prerequisites = TrimTextBlock($prerequisites);
855 if ($prerequisites ne "") {
856 $prerequisites = <<EOF;
857 <refsect1 id="$section_id.prerequisites" role="prerequisites">
858 <title role="prerequisites.title">Prerequisites</title>
864 $derived = TrimTextBlock($derived);
865 if ($derived ne "") {
867 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
868 <title role="derived_interfaces.title">Known Derived Interfaces</title>
874 $functions_synop =~ s/^\n*//g;
875 $functions_synop =~ s/\n+$/\n/g;
876 if ($functions_synop ne '') {
877 $functions_synop = <<EOF;
878 <refsect1 id="$section_id.functions" role="functions_proto">
879 <title role="functions_proto.title">Functions</title>
880 <informaltable pgwide="1" frame="none">
882 <colspec colname="functions_return" colwidth="150px"/>
883 <colspec colname="functions_name"/>
893 $other_synop =~ s/^\n*//g;
894 $other_synop =~ s/\n+$/\n/g;
895 if ($other_synop ne '') {
896 $other_synop = <<EOF;
897 <refsect1 id="$section_id.other" role="other_proto">
898 <title role="other_proto.title">Types and Values</title>
899 <informaltable role="enum_members_table" pgwide="1" frame="none">
901 <colspec colname="name" colwidth="150px"/>
902 <colspec colname="description"/>
912 my $file_changed = &OutputSGMLFile ($filename, $title, $section_id,
914 \$functions_synop, \$other_synop,
915 \$functions_details, \$other_details,
916 \$signals_synop, \$signals_desc,
917 \$args_synop, \$args_desc,
918 \$hierarchy_str, \$interfaces,
920 \$prerequisites, \$derived,
930 $section_includes = "";
931 $functions_synop = "";
933 $functions_details = "";
938 $child_args_synop = "";
939 $style_args_synop = "";
941 $child_args_desc = "";
942 $style_args_desc = "";
946 $implementations = "";
950 } elsif (m/^(\S+)/) {
952 @TRACE@(" Symbol: $symbol in subsection: $subsection\n");
954 # check for duplicate entries
955 if (! defined $symbol_def_line{$symbol}) {
956 my $declaration = $Declarations{$symbol};
957 if (defined ($declaration)) {
958 if (&CheckIsObject ($symbol)) {
959 push @file_objects, $symbol;
961 # We don't want standard macros/functions of GObjects,
962 # or private declarations.
963 if ($subsection ne "Standard" && $subsection ne "Private") {
964 my ($synop, $desc) = &OutputDeclaration ($symbol,
966 my $type = $DeclarationTypes {$symbol};
968 if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
969 $functions_synop .= $synop;
970 $functions_details .= $desc;
971 } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
972 $functions_synop .= $synop;
973 $functions_details .= $desc;
975 $other_synop .= $synop;
976 $other_details .= $desc;
979 my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
980 my ($arg_synop, $child_arg_synop, $style_arg_synop,
981 $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
982 my $ifaces = &GetInterfaces ($symbol);
983 my $impls = &GetImplementations ($symbol);
984 my $prereqs = &GetPrerequisites ($symbol);
985 my $der = &GetDerived ($symbol);
986 @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
988 $signals_synop .= $sig_synop;
989 $signals_desc .= $sig_desc;
990 $args_synop .= $arg_synop;
991 $child_args_synop .= $child_arg_synop;
992 $style_args_synop .= $style_arg_synop;
993 $args_desc .= $arg_desc;
994 $child_args_desc .= $child_arg_desc;
995 $style_args_desc .= $style_arg_desc;
996 $interfaces .= $ifaces;
997 $implementations .= $impls;
998 $prerequisites .= $prereqs;
1001 # Note that the declaration has been output.
1002 $DeclarationOutput{$symbol} = 1;
1003 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
1004 $UndeclaredSymbols{$symbol} = 1;
1005 &LogWarning ($file, $., "No declaration found for $symbol.");
1008 $symbol_def_line{$symbol}=$.;
1010 if ($section_id eq "") {
1011 if($title eq "" && $filename eq "") {
1012 &LogWarning ($file, $., "Section has no title and no file.");
1014 # FIXME: one of those would be enough
1015 # filename should be an internal detail for gtk-doc
1018 } elsif ($filename eq "") {
1021 $filename =~ s/\s/_/g;
1023 $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1024 if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1025 # Remove trailing blanks and use as is
1026 $section_id =~ s/\s+$//;
1027 } elsif (&CheckIsObject ($title)) {
1028 # GObjects use their class name as the ID.
1029 $section_id = &CreateValidSGMLID ($title);
1031 $section_id = &CreateValidSGMLID ("$MODULE-$title");
1034 $SymbolSection{$symbol}=$title;
1035 $SymbolSectionId{$symbol}=$section_id;
1038 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1039 "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1045 &OutputMissingDocumentation;
1046 &OutputUndeclaredSymbols;
1047 &OutputUnusedSymbols;
1049 if ($OUTPUT_ALL_SYMBOLS) {
1052 if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1053 &OutputSymbolsWithoutSince;
1056 for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1057 my $file_changed = &OutputExtraFile ($filename);
1058 if ($file_changed) {
1063 &OutputBook ($book_top, $book_bottom);
1068 #############################################################################
1069 # Function : OutputIndex
1070 # Description : This writes an indexlist that can be included into the main-
1071 # document into an <index> tag.
1072 #############################################################################
1075 my ($basename, $apiindexref ) = @_;
1076 my %apiindex = %{$apiindexref};
1077 my $old_index = "$SGML_OUTPUT_DIR/$basename.xml";
1078 my $new_index = "$SGML_OUTPUT_DIR/$basename.new";
1079 my $lastletter = " ";
1084 open (OUTPUT, ">$new_index")
1085 || die "Can't create $new_index";
1087 my $header = $doctype_header;
1088 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE indexdiv/;
1090 print (OUTPUT "$header<indexdiv>\n");
1092 @TRACE@("generate $basename index (".%apiindex." entries)\n");
1094 # do a case insensitive sort while chopping off the prefix
1096 sort { $$a{criteria} cmp $$b{criteria} }
1097 map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1100 $symbol = $$hash{original};
1101 if (defined($$hash{short})) {
1102 $short_symbol = $$hash{short};
1104 $short_symbol = $symbol;
1107 # generate a short symbol description
1108 my $symbol_desc = "";
1109 my $symbol_section = "";
1110 my $symbol_section_id = "";
1111 my $symbol_type = "";
1112 if (defined($DeclarationTypes{$symbol})) {
1113 $symbol_type = lc($DeclarationTypes{$symbol});
1115 if ($symbol_type eq "") {
1116 @TRACE@("trying symbol $symbol\n");
1117 if ($symbol =~ m/(.*)::(.*)/) {
1121 @TRACE@(" trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1122 for ($i = 0; $i <= $#SignalNames; $i++) {
1123 if ($SignalNames[$i] eq $osym) {
1124 $symbol_type = "object signal";
1125 if (defined($SymbolSection{$oname})) {
1126 $symbol_section = $SymbolSection{$oname};
1127 $symbol_section_id = $SymbolSectionId{$oname};
1132 } elsif ($symbol =~ m/(.*):(.*)/) {
1136 @TRACE@(" trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1137 for ($i = 0; $i <= $#ArgNames; $i++) {
1138 @TRACE@(" ".$ArgNames[$i]."\n");
1139 if ($ArgNames[$i] eq $osym) {
1140 $symbol_type = "object property";
1141 if (defined($SymbolSection{$oname})) {
1142 $symbol_section = $SymbolSection{$oname};
1143 $symbol_section_id = $SymbolSectionId{$oname};
1150 if (defined($SymbolSection{$symbol})) {
1151 $symbol_section = $SymbolSection{$symbol};
1152 $symbol_section_id = $SymbolSectionId{$symbol};
1155 if ($symbol_type ne "") {
1156 $symbol_desc=", $symbol_type";
1157 if ($symbol_section ne "") {
1158 $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1159 #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1163 my $curletter = uc(substr($short_symbol,0,1));
1164 my $id = $apiindex{$symbol};
1166 @TRACE@(" add symbol $symbol with $id to index in section $curletter\n");
1168 if ($curletter ne $lastletter) {
1169 $lastletter = $curletter;
1171 if ($divopen == 1) {
1172 print (OUTPUT "</indexdiv>\n");
1174 print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1178 print (OUTPUT <<EOF);
1179 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1183 if ($divopen == 1) {
1184 print (OUTPUT "</indexdiv>\n");
1186 print (OUTPUT "</indexdiv>\n");
1189 &UpdateFileIfChanged ($old_index, $new_index, 0);
1193 #############################################################################
1194 # Function : OutputIndexFull
1195 # Description : This writes the full api indexlist that can be included into the
1196 # main document into an <index> tag.
1197 #############################################################################
1199 sub OutputIndexFull {
1200 &OutputIndex ("api-index-full", \%IndexEntriesFull);
1204 #############################################################################
1205 # Function : OutputDeprecatedIndex
1206 # Description : This writes the deprecated api indexlist that can be included
1207 # into the main document into an <index> tag.
1208 #############################################################################
1210 sub OutputDeprecatedIndex {
1211 &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1215 #############################################################################
1216 # Function : OutputSinceIndexes
1217 # Description : This writes the 'since' api indexlists that can be included into
1218 # the main document into an <index> tag.
1219 #############################################################################
1221 sub OutputSinceIndexes {
1222 my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1224 foreach my $version (@sinces) {
1225 @TRACE@("Since : [$version]\n");
1226 # TODO make filtered hash
1227 #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1228 my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1230 &OutputIndex ("api-index-$version", \%index);
1234 #############################################################################
1235 # Function : OutputAnnotationGlossary
1236 # Description : This writes a glossary of the used annotation terms into a
1237 # separate glossary file that can be included into the main
1239 #############################################################################
1241 sub OutputAnnotationGlossary {
1242 my $old_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.xml";
1243 my $new_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.new";
1244 my $lastletter = " ";
1247 # if there are no annotations used return
1248 return if (! keys(%AnnotationsUsed));
1250 # add acronyms that are referenced from acronym text
1252 foreach my $annotation (keys(%AnnotationsUsed)) {
1253 if(defined($AnnotationDefinition{$annotation})) {
1254 if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1255 if (!exists($AnnotationsUsed{$1})) {
1256 $AnnotationsUsed{$1} = 1;
1263 open (OUTPUT, ">$new_glossary")
1264 || die "Can't create $new_glossary";
1266 my $header = $doctype_header;
1267 $header =~ s/<!DOCTYPE \w+/<!DOCTYPE glossary/;
1269 print (OUTPUT <<EOF);
1271 <glossary id="annotation-glossary">
1272 <title>Annotation Glossary</title>
1275 foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1276 if(defined($AnnotationDefinition{$annotation})) {
1277 my $def = $AnnotationDefinition{$annotation};
1278 my $curletter = uc(substr($annotation,0,1));
1280 if ($curletter ne $lastletter) {
1281 $lastletter = $curletter;
1283 if ($divopen == 1) {
1284 print (OUTPUT "</glossdiv>\n");
1286 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1289 print (OUTPUT <<EOF);
1291 <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1300 if ($divopen == 1) {
1301 print (OUTPUT "</glossdiv>\n");
1303 print (OUTPUT "</glossary>\n");
1306 &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1309 #############################################################################
1310 # Function : ReadKnownSymbols
1311 # Description : This collects the names of non-private symbols from the
1312 # $MODULE-sections.txt file.
1313 # Arguments : $file - the $MODULE-sections.txt file which contains all of
1314 # the functions/macros/structs etc. being documented, organised
1315 # into sections and subsections.
1316 #############################################################################
1318 sub ReadKnownSymbols {
1321 my $subsection = "";
1323 @TRACE@("Reading: $file\n");
1325 || die "Can't open $file: $!";
1331 } elsif (m/^<SECTION>/) {
1334 } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1337 } elsif (m/^<SUBSECTION>/) {
1340 } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1343 } elsif (m/^<FILE>(.*)<\/FILE>/) {
1344 $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1345 $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1348 } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1351 } elsif (m/^<\/SECTION>/) {
1354 } elsif (m/^(\S+)/) {
1357 if ($subsection ne "Standard" && $subsection ne "Private") {
1358 $KnownSymbols{$symbol} = 1;
1361 $KnownSymbols{$symbol} = 0;
1369 #############################################################################
1370 # Function : OutputDeclaration
1371 # Description : Returns the synopsis and detailed description DocBook
1372 # describing one function/macro etc.
1373 # Arguments : $symbol - the name of the function/macro begin described.
1374 # $declaration - the declaration of the function/macro.
1375 #############################################################################
1377 sub OutputDeclaration {
1378 my ($symbol, $declaration) = @_;
1380 my $type = $DeclarationTypes {$symbol};
1381 if ($type eq 'MACRO') {
1382 return &OutputMacro ($symbol, $declaration);
1383 } elsif ($type eq 'TYPEDEF') {
1384 return &OutputTypedef ($symbol, $declaration);
1385 } elsif ($type eq 'STRUCT') {
1386 return &OutputStruct ($symbol, $declaration);
1387 } elsif ($type eq 'ENUM') {
1388 return &OutputEnum ($symbol, $declaration);
1389 } elsif ($type eq 'UNION') {
1390 return &OutputUnion ($symbol, $declaration);
1391 } elsif ($type eq 'VARIABLE') {
1392 return &OutputVariable ($symbol, $declaration);
1393 } elsif ($type eq 'FUNCTION') {
1394 return &OutputFunction ($symbol, $declaration, $type);
1395 } elsif ($type eq 'USER_FUNCTION') {
1396 return &OutputFunction ($symbol, $declaration, $type);
1398 die "Unknown symbol type";
1403 #############################################################################
1404 # Function : OutputSymbolTraits
1405 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1406 # Arguments : $symbol - the name of the function/macro begin described.
1407 #############################################################################
1409 sub OutputSymbolTraits {
1413 if (exists $Since{$symbol}) {
1414 $desc .= "<para role=\"since\">Since $Since{$symbol}</para>";
1416 if (exists $StabilityLevel{$symbol}) {
1417 my $stability = $StabilityLevel{$symbol};
1418 $AnnotationsUsed{$stability} = 1;
1419 $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1424 #############################################################################
1425 # Function : Output{Symbol,Section}ExtraLinks
1426 # Description : Returns extralinks for the symbol (if enabled).
1427 # Arguments : $symbol - the name of the function/macro begin described.
1428 #############################################################################
1432 return undef unless defined $text;
1434 # Build a char to hex map
1437 $escapes{chr($_)} = sprintf("%%%02X", $_);
1440 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1441 $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1446 sub OutputSymbolExtraLinks {
1450 if (0) { # NEW FEATURE: needs configurability
1451 my $sstr = &uri_escape($symbol);
1452 my $mstr = &uri_escape($MODULE);
1454 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1455 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$sstr">edit documentation</ulink>
1461 sub OutputSectionExtraLinks {
1462 my ($symbol,$docsymbol) = @_;
1465 if (0) { # NEW FEATURE: needs configurability
1466 my $sstr = &uri_escape($symbol);
1467 my $mstr = &uri_escape($MODULE);
1468 my $dsstr = &uri_escape($docsymbol);
1470 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1471 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$dsstr">edit documentation</ulink>
1478 #############################################################################
1479 # Function : OutputMacro
1480 # Description : Returns the synopsis and detailed description of a macro.
1481 # Arguments : $symbol - the macro.
1482 # $declaration - the declaration of the macro.
1483 #############################################################################
1486 my ($symbol, $declaration) = @_;
1487 my $id = &CreateValidSGMLID ($symbol);
1488 my $condition = &MakeConditionDescription ($symbol);
1489 my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1492 my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1493 my $title = $symbol . (@fields ? "()" : "");
1495 $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1496 $desc .= MakeIndexterms($symbol, $id);
1498 $desc .= OutputSymbolExtraLinks($symbol);
1501 $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1503 $synop .= "</entry></row>\n";
1505 # Don't output the macro definition if is is a conditional macro or it
1506 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1507 # longer than 2 lines, otherwise we get lots of complicated macros like
1509 if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1510 && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1511 my $decl_out = &CreateValidSGML ($declaration);
1512 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1514 $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1515 if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1517 my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1518 # Align each line so that if should all line up OK.
1519 $args =~ s/\n/\n$pad/gm;
1520 $desc .= &CreateValidSGML ($args);
1522 $desc .= "</programlisting>\n";
1525 $desc .= &MakeDeprecationNote($symbol);
1527 my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1529 if (defined ($SymbolDocs{$symbol})) {
1530 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1531 $desc .= $symbol_docs;
1534 $desc .= $parameters;
1535 $desc .= OutputSymbolTraits ($symbol);
1536 $desc .= "</refsect2>\n";
1537 return ($synop, $desc);
1541 #############################################################################
1542 # Function : OutputTypedef
1543 # Description : Returns the synopsis and detailed description of a typedef.
1544 # Arguments : $symbol - the typedef.
1545 # $declaration - the declaration of the typedef,
1546 # e.g. 'typedef unsigned int guint;'
1547 #############################################################################
1550 my ($symbol, $declaration) = @_;
1551 my $id = &CreateValidSGMLID ($symbol);
1552 my $condition = &MakeConditionDescription ($symbol);
1553 my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1554 my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1556 $desc .= MakeIndexterms($symbol, $id);
1558 $desc .= OutputSymbolExtraLinks($symbol);
1560 if (!defined ($DeclarationConditional{$symbol})) {
1561 my $decl_out = &CreateValidSGML ($declaration);
1562 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1565 $desc .= &MakeDeprecationNote($symbol);
1567 if (defined ($SymbolDocs{$symbol})) {
1568 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1570 $desc .= OutputSymbolTraits ($symbol);
1571 $desc .= "</refsect2>\n";
1572 return ($synop, $desc);
1576 #############################################################################
1577 # Function : OutputStruct
1578 # Description : Returns the synopsis and detailed description of a struct.
1579 # We check if it is a object struct, and if so we only output
1580 # parts of it that are noted as public fields.
1581 # We also use a different SGML ID for object structs, since the
1582 # original ID is used for the entire RefEntry.
1583 # Arguments : $symbol - the struct.
1584 # $declaration - the declaration of the struct.
1585 #############################################################################
1588 my ($symbol, $declaration) = @_;
1591 my $default_to_public = 1;
1592 if (&CheckIsObject ($symbol)) {
1593 @TRACE@("Found struct gtype: $symbol\n");
1595 $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1601 $id = &CreateValidSGMLID ($symbol . "_struct");
1602 $condition = &MakeConditionDescription ($symbol . "_struct");
1604 $id = &CreateValidSGMLID ($symbol);
1605 $condition = &MakeConditionDescription ($symbol);
1608 # Determine if it is a simple struct or it also has a typedef.
1609 my $has_typedef = 0;
1610 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1617 # For structs with typedefs we just output the struct name.
1619 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1621 $type_output = "struct";
1622 $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1624 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1626 $desc .= MakeIndexterms($symbol, $id);
1628 $desc .= OutputSymbolExtraLinks($symbol);
1630 # Form a pretty-printed, private-data-removed form of the declaration
1633 if ($declaration =~ m/^\s*$/) {
1634 @TRACE@("Found opaque struct: $symbol\n");
1635 $decl_out = "typedef struct _$symbol $symbol;";
1636 } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1637 @TRACE@("Found opaque struct: $symbol\n");
1638 $decl_out = "struct $symbol;";
1640 my $public = $default_to_public;
1641 my $new_declaration = "";
1643 my $decl = $declaration;
1645 if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1646 my $struct_contents = $2;
1648 foreach $decl_line (split (/\n/, $struct_contents)) {
1649 @TRACE@("Struct line: $decl_line\n");
1650 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1652 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1655 $new_declaration .= $decl_line . "\n";
1659 if ($new_declaration) {
1660 # Strip any blank lines off the ends.
1661 $new_declaration =~ s/^\s*\n//;
1662 $new_declaration =~ s/\n\s*$/\n/;
1665 $decl_out = "typedef struct {\n" . $new_declaration
1668 $decl_out = "struct $symbol {\n" . $new_declaration
1673 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1674 "Couldn't parse struct:\n$declaration");
1677 # If we couldn't parse the struct or it was all private, output an
1678 # empty struct declaration.
1679 if ($decl_out eq "") {
1681 $decl_out = "typedef struct _$symbol $symbol;";
1683 $decl_out = "struct $symbol;";
1688 $decl_out = &CreateValidSGML ($decl_out);
1689 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1691 $desc .= &MakeDeprecationNote($symbol);
1693 if (defined ($SymbolDocs{$symbol})) {
1694 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1697 # Create a table of fields and descriptions
1699 # FIXME: Inserting  's into the produced type declarations here would
1700 # improve the output in most situations ... except for function
1701 # members of structs!
1702 my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1705 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1707 my $params = $SymbolParams{$symbol};
1709 # If no parameters are filled in, we don't generate the description
1710 # table, for backwards compatibility.
1713 if (defined $params) {
1714 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1715 if ($params->[$i] =~ /\S/) {
1723 my %field_descrs = @$params;
1724 my $missing_parameters = "";
1725 my $unused_parameters = "";
1728 <refsect3 role="struct_members">\n<title>Members</title>
1729 <informaltable role="struct_members_table" pgwide="1" frame="none">
1731 <colspec colname="struct_members_name" colwidth="300px"/>
1732 <colspec colname="struct_members_description"/>
1733 <colspec colname="struct_members_annotations" colwidth="200px"/>
1738 my $field_name = shift @fields;
1739 my $text = shift @fields;
1740 my $field_descr = $field_descrs{$field_name};
1741 my $param_annotations = "";
1743 $desc .= "<row><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1744 if (defined $field_descr) {
1745 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1746 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1748 $field_descr =~ s/^(\s|\n)+//msg;
1749 $field_descr =~ s/(\s|\n)+$//msg;
1750 $desc .= "<listitem>$field_descr</listitem>\n";
1751 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1752 delete $field_descrs{$field_name};
1754 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1755 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1756 if ($missing_parameters ne "") {
1757 $missing_parameters .= ", ".$field_name;
1759 $missing_parameters = $field_name;
1761 $desc .= "<entry /><entry />\n";
1763 $desc .= "</row>\n";
1765 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1766 foreach my $field_name (keys %field_descrs) {
1767 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1768 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1769 if ($unused_parameters ne "") {
1770 $unused_parameters .= ", ".$field_name;
1772 $unused_parameters = $field_name;
1776 # remember missing/unused parameters (needed in tmpl-free build)
1777 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1778 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1780 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1781 $AllUnusedSymbols{$symbol}=$unused_parameters;
1785 if (scalar(@fields) > 0) {
1786 if (! exists ($AllIncompleteSymbols{$symbol})) {
1787 $AllIncompleteSymbols{$symbol}="<items>";
1788 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1789 "Field descriptions for struct $symbol are missing in source code comment block.");
1790 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1795 $desc .= OutputSymbolTraits ($symbol);
1796 $desc .= "</refsect2>\n";
1797 return ($synop, $desc);
1801 #############################################################################
1802 # Function : OutputUnion
1803 # Description : Returns the synopsis and detailed description of a union.
1804 # Arguments : $symbol - the union.
1805 # $declaration - the declaration of the union.
1806 #############################################################################
1809 my ($symbol, $declaration) = @_;
1812 if (&CheckIsObject ($symbol)) {
1813 @TRACE@("Found union gtype: $symbol\n");
1820 $id = &CreateValidSGMLID ($symbol . "_union");
1821 $condition = &MakeConditionDescription ($symbol . "_union");
1823 $id = &CreateValidSGMLID ($symbol);
1824 $condition = &MakeConditionDescription ($symbol);
1827 # Determine if it is a simple struct or it also has a typedef.
1828 my $has_typedef = 0;
1829 if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1836 # For unions with typedefs we just output the union name.
1838 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1840 $type_output = "union";
1841 $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1843 my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1845 $desc .= MakeIndexterms($symbol, $id);
1847 $desc .= OutputSymbolExtraLinks($symbol);
1848 $desc .= &MakeDeprecationNote($symbol);
1850 if (defined ($SymbolDocs{$symbol})) {
1851 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1854 # Create a table of fields and descriptions
1856 # FIXME: Inserting  's into the produced type declarations here would
1857 # improve the output in most situations ... except for function
1858 # members of structs!
1859 my @fields = ParseStructDeclaration($declaration, 0,
1862 "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1864 my $params = $SymbolParams{$symbol};
1866 # If no parameters are filled in, we don't generate the description
1867 # table, for backwards compatibility
1870 if (defined $params) {
1871 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1872 if ($params->[$i] =~ /\S/) {
1880 my %field_descrs = @$params;
1881 my $missing_parameters = "";
1882 my $unused_parameters = "";
1885 <refsect3 role="union_members">\n<title>Members</title>
1886 <informaltable role="union_members_table" pgwide="1" frame="none">
1888 <colspec colname="union_members_name" colwidth="300px"/>
1889 <colspec colname="union_members_description"/>
1890 <colspec colname="union_members_annotations" colwidth="200px"/>
1895 my $field_name = shift @fields;
1896 my $text = shift @fields;
1897 my $field_descr = $field_descrs{$field_name};
1898 my $param_annotations = "";
1900 $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1901 if (defined $field_descr) {
1902 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1903 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1906 $field_descr =~ s/^(\s|\n)+//msg;
1907 $field_descr =~ s/(\s|\n)+$//msg;
1908 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1909 delete $field_descrs{$field_name};
1911 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1912 "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1913 if ($missing_parameters ne "") {
1914 $missing_parameters .= ", ".$field_name;
1916 $missing_parameters = $field_name;
1918 $desc .= "<entry /><entry />\n";
1920 $desc .= "</row>\n";
1922 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1923 foreach my $field_name (keys %field_descrs) {
1924 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1925 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1926 if ($unused_parameters ne "") {
1927 $unused_parameters .= ", ".$field_name;
1929 $unused_parameters = $field_name;
1933 # remember missing/unused parameters (needed in tmpl-free build)
1934 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1935 $AllIncompleteSymbols{$symbol}=$missing_parameters;
1937 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1938 $AllUnusedSymbols{$symbol}=$unused_parameters;
1942 if (scalar(@fields) > 0) {
1943 if (! exists ($AllIncompleteSymbols{$symbol})) {
1944 $AllIncompleteSymbols{$symbol}="<items>";
1945 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1946 "Field descriptions for union $symbol are missing in source code comment block.");
1947 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1952 $desc .= OutputSymbolTraits ($symbol);
1953 $desc .= "</refsect2>\n";
1954 return ($synop, $desc);
1958 #############################################################################
1959 # Function : OutputEnum
1960 # Description : Returns the synopsis and detailed description of a enum.
1961 # Arguments : $symbol - the enum.
1962 # $declaration - the declaration of the enum.
1963 #############################################################################
1966 my ($symbol, $declaration) = @_;
1969 if (&CheckIsObject ($symbol)) {
1970 @TRACE@("Found enum gtype: $symbol\n");
1977 $id = &CreateValidSGMLID ($symbol . "_enum");
1978 $condition = &MakeConditionDescription ($symbol . "_enum");
1980 $id = &CreateValidSGMLID ($symbol);
1981 $condition = &MakeConditionDescription ($symbol);
1984 my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1985 my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1987 $desc .= MakeIndexterms($symbol, $id);
1989 $desc .= OutputSymbolExtraLinks($symbol);
1990 $desc .= &MakeDeprecationNote($symbol);
1992 if (defined ($SymbolDocs{$symbol})) {
1993 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1996 # Create a table of fields and descriptions
1998 my @fields = ParseEnumDeclaration($declaration);
1999 my $params = $SymbolParams{$symbol};
2001 # If nothing at all is documented log a single summary warning at the end.
2002 # Otherwise, warn about each undocumented item.
2005 if (defined $params) {
2006 for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
2007 if ($params->[$i] =~ /\S/) {
2014 my %field_descrs = (defined $params ? @$params : ());
2015 my $missing_parameters = "";
2016 my $unused_parameters = "";
2019 <refsect3 role="enum_members">\n<title>Members</title>
2020 <informaltable role="enum_members_table" pgwide="1" frame="none">
2022 <colspec colname="enum_members_name" colwidth="300px"/>
2023 <colspec colname="enum_members_description"/>
2024 <colspec colname="enum_members_annotations" colwidth="200px"/>
2028 for my $field_name (@fields) {
2029 my $field_descr = $field_descrs{$field_name};
2030 my $param_annotations = "";
2032 $id = &CreateValidSGMLID ($field_name);
2033 $condition = &MakeConditionDescription ($field_name);
2034 $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2035 if (defined $field_descr) {
2036 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2037 $field_descr = &ConvertMarkDown($symbol, $field_descr);
2038 $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2039 delete $field_descrs{$field_name};
2042 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2043 "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2044 if ($missing_parameters ne "") {
2045 $missing_parameters .= ", ".$field_name;
2047 $missing_parameters = $field_name;
2050 $desc .= "<entry /><entry />\n";
2052 $desc .= "</row>\n";
2054 $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2055 foreach my $field_name (keys %field_descrs) {
2056 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2057 "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2058 if ($unused_parameters ne "") {
2059 $unused_parameters .= ", ".$field_name;
2061 $unused_parameters = $field_name;
2065 # remember missing/unused parameters (needed in tmpl-free build)
2066 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2067 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2069 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2070 $AllUnusedSymbols{$symbol}=$unused_parameters;
2074 if (scalar(@fields) > 0) {
2075 if (! exists ($AllIncompleteSymbols{$symbol})) {
2076 $AllIncompleteSymbols{$symbol}="<items>";
2077 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2078 "Value descriptions for $symbol are missing in source code comment block.");
2083 $desc .= OutputSymbolTraits ($symbol);
2084 $desc .= "</refsect2>\n";
2085 return ($synop, $desc);
2089 #############################################################################
2090 # Function : OutputVariable
2091 # Description : Returns the synopsis and detailed description of a variable.
2092 # Arguments : $symbol - the extern'ed variable.
2093 # $declaration - the declaration of the variable.
2094 #############################################################################
2096 sub OutputVariable {
2097 my ($symbol, $declaration) = @_;
2098 my $id = &CreateValidSGMLID ($symbol);
2099 my $condition = &MakeConditionDescription ($symbol);
2101 @TRACE@("ouputing variable: '$symbol' '$declaration'");
2104 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*;/) {
2105 my $mod1 = defined ($1) ? $1 : "";
2106 my $ptr = defined ($3) ? $3 : "";
2107 my $space = defined ($4) ? $4 : "";
2108 my $mod2 = defined ($5) ? $5 : "";
2109 $type_output = "extern $mod1$ptr$space$mod2";
2110 } 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*=/) {
2111 my $mod1 = defined ($1) ? $1 : "";
2112 my $ptr = defined ($3) ? $3 : "";
2113 my $space = defined ($4) ? $4 : "";
2114 my $mod2 = defined ($5) ? $5 : "";
2115 $type_output = "$mod1$ptr$space$mod2";
2117 $type_output = "extern";
2119 my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2121 my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2123 $desc .= MakeIndexterms($symbol, $id);
2125 $desc .= OutputSymbolExtraLinks($symbol);
2127 my $decl_out = &CreateValidSGML ($declaration);
2128 $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2130 $desc .= &MakeDeprecationNote($symbol);
2132 if (defined ($SymbolDocs{$symbol})) {
2133 $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2135 $desc .= OutputSymbolTraits ($symbol);
2136 $desc .= "</refsect2>\n";
2137 return ($synop, $desc);
2141 #############################################################################
2142 # Function : OutputFunction
2143 # Description : Returns the synopsis and detailed description of a function.
2144 # Arguments : $symbol - the function.
2145 # $declaration - the declaration of the function.
2146 #############################################################################
2148 sub OutputFunction {
2149 my ($symbol, $declaration, $symbol_type) = @_;
2150 my $id = &CreateValidSGMLID ($symbol);
2151 my $condition = &MakeConditionDescription ($symbol);
2153 # Take out the return type $1 $2 $3
2154 $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//;
2155 my $type_modifier = defined($1) ? $1 : "";
2158 # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2159 $pointer =~ s/\s+$//;
2160 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2162 #if ($symbol_type eq 'USER_FUNCTION') {
2163 # $start = "typedef ";
2166 # We output const rather than G_CONST_RETURN.
2167 $type_modifier =~ s/G_CONST_RETURN/const/g;
2168 $pointer =~ s/G_CONST_RETURN/const/g;
2169 $pointer =~ s/^\s+/ /g;
2171 my $ret_type_output;
2172 $ret_type_output = "$start$type_modifier$xref$pointer\n";
2175 $indent_len = length ($symbol) + 2;
2176 my $char1 = my $char2 = my $char3 = "";
2177 if ($symbol_type eq 'USER_FUNCTION') {
2179 $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2181 $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2184 my ($symbol_output, $symbol_desc_output);
2185 $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2186 if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2187 $symbol_desc_output = "$char1$char2$symbol$char3 ";
2189 $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2190 $symbol_desc_output = "$char1$char2$symbol$char3\n"
2191 . (' ' x ($indent_len - 1));
2194 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";
2196 my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol} ()</title>\n";
2198 $desc .= MakeIndexterms($symbol, $id);
2200 $desc .= OutputSymbolExtraLinks($symbol);
2202 $desc .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2204 my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2206 &tagify($_[0],"parameter");
2209 for (my $i = 1; $i <= $#fields; $i += 2) {
2210 my $field_name = $fields[$i];
2213 $desc .= "$field_name";
2216 . (' ' x $indent_len)
2222 $desc .= ");</programlisting>\n";
2224 $desc .= &MakeDeprecationNote($symbol);
2226 my $parameters = &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2228 if (defined ($SymbolDocs{$symbol})) {
2229 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2230 $desc .= $symbol_docs;
2233 $desc .= $parameters;
2234 $desc .= OutputSymbolTraits ($symbol);
2235 $desc .= "</refsect2>\n";
2236 return ($synop, $desc);
2240 #############################################################################
2241 # Function : OutputParamDescriptions
2242 # Description : Returns the DocBook output describing the parameters of a
2243 # function, macro or signal handler.
2244 # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2245 # handlers have an implicit user_data parameter last.
2246 # $symbol - the name of the function/macro being described.
2247 # @fields - parsed fields from the declaration, used to determine
2248 # undocumented/unused entries
2249 #############################################################################
2251 sub OutputParamDescriptions {
2252 my ($symbol_type, $symbol, @fields) = @_;
2254 my $params = $SymbolParams{$symbol};
2256 my %field_descrs = ();
2259 %field_descrs = @fields;
2260 delete $field_descrs{"void"};
2261 delete $field_descrs{"Returns"};
2264 if (defined $params) {
2266 my $params_desc = "";
2267 my $missing_parameters = "";
2268 my $unused_parameters = "";
2271 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2272 my $param_name = $$params[$j];
2273 my $param_desc = $$params[$j + 1];
2274 my $param_annotations = "";
2276 ($param_desc,$param_annotations) = & ExpandAnnotation($symbol, $param_desc);
2277 $param_desc = &ConvertMarkDown($symbol, $param_desc);
2279 $param_desc =~ s/^(\s|\n)+//msg;
2280 $param_desc =~ s/(\s|\n)+$//msg;
2281 if ($param_name eq "Returns") {
2282 $returns = "$param_desc\n<para>$param_annotations</para>";
2283 } elsif ($param_name eq "void") {
2284 # FIXME: &LogWarning()?
2285 @TRACE@("!!!! void in params for $symbol?\n");
2288 if (!defined $field_descrs{$param_name}) {
2289 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2290 "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2291 if ($unused_parameters ne "") {
2292 $unused_parameters .= ", ".$param_name;
2294 $unused_parameters = $param_name;
2297 delete $field_descrs{$param_name};
2300 if($param_desc ne "") {
2301 $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";
2306 foreach my $param_name (keys %field_descrs) {
2307 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2308 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2309 if ($missing_parameters ne "") {
2310 $missing_parameters .= ", ".$param_name;
2312 $missing_parameters = $param_name;
2316 # Signals have an implicit user_data parameter which we describe.
2317 if ($symbol_type eq "SIGNAL") {
2318 $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";
2321 # Start a table if we need one.
2322 if ($params_desc ne "") {
2324 <refsect3 role="parameters">\n<title>Parameters</title>
2325 <informaltable role="parameters_table" pgwide="1" frame="none">
2327 <colspec colname="parameters_name" colwidth="150px"/>
2328 <colspec colname="parameters_description"/>
2329 <colspec colname="parameters_annotations" colwidth="200px"/>
2332 $output .= $params_desc;
2333 $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2336 # Output the returns info last
2337 if ($returns ne "") {
2339 <refsect3 role=\"returns\">\n<title>Returns</title>
2341 $output .= $returns;
2342 $output .= "\n</refsect3>";
2345 # remember missing/unused parameters (needed in tmpl-free build)
2346 if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2347 $AllIncompleteSymbols{$symbol}=$missing_parameters;
2349 if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2350 $AllUnusedSymbols{$symbol}=$unused_parameters;
2353 if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2354 if (! exists ($AllIncompleteSymbols{$symbol})) {
2355 $AllIncompleteSymbols{$symbol}="<parameters>";
2363 #############################################################################
2364 # Function : ParseStabilityLevel
2365 # Description : Parses a stability level and outputs a warning if it isn't
2367 # Arguments : $stability - the stability text.
2368 # $file, $line - context for error message
2369 # $message - description of where the level is from, to use in
2370 # any error message.
2371 # Returns : The parsed stability level string.
2372 #############################################################################
2374 sub ParseStabilityLevel {
2375 my ($stability, $file, $line, $message) = @_;
2377 $stability =~ s/^\s*//;
2378 $stability =~ s/\s*$//;
2379 if ($stability =~ m/^stable$/i) {
2380 $stability = "Stable";
2381 } elsif ($stability =~ m/^unstable$/i) {
2382 $stability = "Unstable";
2383 } elsif ($stability =~ m/^private$/i) {
2384 $stability = "Private";
2386 &LogWarning ($file, $line, "$message is $stability.".
2387 "It should be one of these: Stable, Unstable, or Private.");
2393 #############################################################################
2394 # Function : OutputSGMLFile
2395 # Description : Outputs the final DocBook file for one section.
2396 # Arguments : $file - the name of the file.
2397 # $title - the title from the $MODULE-sections.txt file, which
2398 # will be overridden by the title in the template file.
2399 # $section_id - the SGML id to use for the toplevel tag.
2400 # $includes - comma-separates list of include files added at top of
2401 # synopsis, with '<' '>' around them (if not already enclosed in "").
2402 # $functions_synop - reference to the DocBook for the Functions Synopsis part.
2403 # $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2404 # $functions_details - reference to the DocBook for the Functions Details part.
2405 # $other_details - reference to the DocBook for the Types and Values Details part.
2406 # $signal_synop - reference to the DocBook for the Signal Synopsis part
2407 # $signal_desc - reference to the DocBook for the Signal Description part
2408 # $args_synop - reference to the DocBook for the Arg Synopsis part
2409 # $args_desc - reference to the DocBook for the Arg Description part
2410 # $hierarchy - reference to the DocBook for the Object Hierarchy part
2411 # $interfaces - reference to the DocBook for the Interfaces part
2412 # $implementations - reference to the DocBook for the Known Implementations part
2413 # $prerequisites - reference to the DocBook for the Prerequisites part
2414 # $derived - reference to the DocBook for the Derived Interfaces part
2415 # $file_objects - reference to an array of objects in this file
2416 #############################################################################
2418 sub OutputSGMLFile {
2419 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) = @_;
2421 @TRACE@("Output sgml for file $file with title '$title'\n");
2423 # The edited title overrides the one from the sections file.
2424 my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2425 if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2426 $title = $new_title;
2427 @TRACE@("Found title: $title\n");
2429 my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2430 if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2433 # Don't use ConvertMarkDown here for now since we don't want blocks
2434 $short_desc = &ExpandAbbreviations("$title:Short_description",
2436 @TRACE@("Found short_desc: $short_desc");
2438 my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2439 if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2442 $long_desc = &ConvertMarkDown("$title:Long_description",
2444 @TRACE@("Found long_desc: $long_desc");
2446 my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2447 if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2450 $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2451 @TRACE@("Found see_also: $see_also");
2454 $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2456 my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2457 if (!defined ($stability) || $stability =~ m/^\s*$/) {
2460 $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2461 @TRACE@("Found stability: $stability");
2464 $AnnotationsUsed{$stability} = 1;
2465 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2466 } elsif ($DEFAULT_STABILITY) {
2467 $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2468 $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2471 my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2472 if (!defined ($image) || $image =~ m/^\s*$/) {
2480 if ($image =~ /jpe?g$/i) {
2481 $format = "format='JPEG'";
2482 } elsif ($image =~ /png$/i) {
2483 $format = "format='PNG'";
2484 } elsif ($image =~ /svg$/i) {
2485 $format = "format='SVG'";
2490 $image = " <inlinegraphic fileref='$image' $format/>\n"
2493 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2495 my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2498 my $include_output = "";
2500 $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2502 foreach $include (split (/,/, $includes)) {
2503 if ($include =~ m/^\".+\"$/) {
2504 $include_output .= "#include ${include}\n";
2507 $include =~ s/^\s+|\s+$//gs;
2508 $include_output .= "#include <${include}>\n";
2511 $include_output .= "</synopsis></refsect1>\n";
2514 my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2516 my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT";
2517 my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new";
2519 open (OUTPUT, ">$new_sgml_file")
2520 || die "Can't create $new_sgml_file: $!";
2522 my $object_anchors = "";
2523 foreach my $object (@$file_objects) {
2524 next if ($object eq $section_id);
2525 my $id = CreateValidSGMLID($object);
2526 @TRACE@("Adding anchor for $object\n");
2527 $object_anchors .= "<anchor id=\"$id\"$empty_element_end";
2530 # We used to output this, but is messes up our UpdateFileIfChanged code
2531 # since it changes every day (and it is only used in the man pages):
2532 # "<refentry id="$section_id" revision="$mday $month $year">"
2534 if ($OUTPUT_FORMAT eq "xml") {
2535 print OUTPUT $doctype_header;
2539 <refentry id="$section_id">
2541 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2542 <manvolnum>3</manvolnum>
2545 $image</refmiscinfo>
2548 <refname>$title</refname>
2549 <refpurpose>$short_desc</refpurpose>
2552 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2554 <refsect1 id="$section_id.description" role="desc">
2555 <title role="desc.title">Description</title>
2556 $extralinks$long_desc
2558 <refsect1 id="$section_id.functions_details" role="details">
2559 <title role="details.title">Functions</title>
2562 <refsect1 id="$section_id.other_details" role="details">
2563 <title role="details.title">Types and Values</title>
2566 $$args_desc$$signals_desc$see_also
2571 return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2575 #############################################################################
2576 # Function : OutputExtraFile
2577 # Description : Copies an "extra" DocBook file into the output directory,
2578 # expanding abbreviations
2579 # Arguments : $file - the source file.
2580 #############################################################################
2581 sub OutputExtraFile {
2586 ($basename = $file) =~ s!^.*/!!;
2588 my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename";
2589 my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new";
2593 open(EXTRA_FILE, "<$file") || die "Can't open $file";
2597 $contents = <EXTRA_FILE>;
2600 open (OUTPUT, ">$new_sgml_file")
2601 || die "Can't create $new_sgml_file: $!";
2603 print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2606 return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2608 #############################################################################
2609 # Function : OutputBook
2610 # Description : Outputs the SGML entities that need to be included into the
2611 # main SGML file for the module.
2612 # Arguments : $book_top - the declarations of the entities, which are added
2613 # at the top of the main SGML file.
2614 # $book_bottom - the references to the entities, which are
2615 # added in the main SGML file at the desired position.
2616 #############################################################################
2619 my ($book_top, $book_bottom) = @_;
2621 my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top";
2622 my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new";
2624 open (OUTPUT, ">$new_file")
2625 || die "Can't create $new_file: $!";
2626 print OUTPUT $book_top;
2629 &UpdateFileIfChanged ($old_file, $new_file, 0);
2632 $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom";
2633 $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new";
2635 open (OUTPUT, ">$new_file")
2636 || die "Can't create $new_file: $!";
2637 print OUTPUT $book_bottom;
2640 &UpdateFileIfChanged ($old_file, $new_file, 0);
2643 # If the main SGML/XML file hasn't been created yet, we create it here.
2644 # The user can tweak it later.
2645 if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2646 open (OUTPUT, ">$MAIN_SGML_FILE")
2647 || die "Can't create $MAIN_SGML_FILE: $!";
2649 if ($OUTPUT_FORMAT eq "xml") {
2651 <?xml version="1.0"?>
2652 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2653 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2655 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
2661 <!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
2670 <title>$MODULE Reference Manual</title>
2672 for $MODULE [VERSION].
2673 The latest version of this documentation can be found on-line at
2674 <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2679 <title>[Insert title here]</title>
2683 if (-e $OBJECT_TREE_FILE) {
2685 <chapter id="object-tree">
2686 <title>Object Hierarchy</title>
2687 <xi:include href="xml/tree_index.sgml"/>
2693 <index id="api-index-full">
2694 <title>API Index</title>
2695 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2697 <index id="deprecated-api-index" role="deprecated">
2698 <title>Index of deprecated API</title>
2699 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2702 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2711 #############################################################################
2712 # Function : CreateValidSGML
2713 # Description : This turns any chars which are used in SGML into entities,
2714 # e.g. '<' into '<'
2715 # Arguments : $text - the text to turn into proper SGML.
2716 #############################################################################
2718 sub CreateValidSGML {
2720 $text =~ s/&/&/g; # Do this first, or the others get messed up.
2721 $text =~ s/</</g;
2722 $text =~ s/>/>/g;
2723 # browers render single tabs inconsistently
2724 $text =~ s/([^\s])\t([^\s])/$1 $2/g;
2728 #############################################################################
2729 # Function : ConvertSGMLChars
2730 # Description : This is used for text in source code comment blocks, to turn
2731 # chars which are used in SGML into entities, e.g. '<' into
2732 # '<'. Depending on $INLINE_MARKUP_MODE, this is done
2733 # unconditionally or only if the character doesn't seem to be
2734 # part of an SGML construct (tag or entity reference).
2735 # Arguments : $text - the text to turn into proper SGML.
2736 #############################################################################
2738 sub ConvertSGMLChars {
2739 my ($symbol, $text) = @_;
2741 if ($INLINE_MARKUP_MODE) {
2742 # For the XML/SGML mode only convert to entities outside CDATA sections.
2743 return &ModifyXMLElements ($text, $symbol,
2744 "<!\\[CDATA\\[|<programlisting[^>]*>",
2745 \&ConvertSGMLCharsEndTag,
2746 \&ConvertSGMLCharsCallback);
2748 # For the simple non-sgml mode, convert to entities everywhere.
2750 # First, convert freestanding & to &
2751 $text =~ s/&(?![a-zA-Z#]+;)/&/g;
2752 $text =~ s/</</g;
2753 # Allow ">" at beginning of string for blockquote markdown
2754 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2761 sub ConvertSGMLCharsEndTag {
2762 if ($_[0] eq "<!\[CDATA\[") {
2765 return "</programlisting>";
2769 sub ConvertSGMLCharsCallback {
2770 my ($text, $symbol, $tag) = @_;
2772 if ($tag =~ m/^<programlisting/) {
2773 # We can handle <programlisting> specially here.
2774 return &ModifyXMLElements ($text, $symbol,
2776 \&ConvertSGMLCharsEndTag,
2777 \&ConvertSGMLCharsCallback2);
2778 } elsif ($tag eq "") {
2779 # If we're not in CDATA convert to entities.
2780 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2781 $text =~ s/<(?![a-zA-Z\/!])/</g;
2782 # Allow ">" at beginning of string for blockquote markdown
2783 $text =~ s/(?<=[^\w\n"'\/-])>/>/g;
2785 # Handle "#include <xxxxx>"
2786 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2792 sub ConvertSGMLCharsCallback2 {
2793 my ($text, $symbol, $tag) = @_;
2795 # If we're not in CDATA convert to entities.
2796 # We could handle <programlisting> differently, though I'm not sure it helps.
2798 # replace only if its not a tag
2799 $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up.
2800 $text =~ s/<(?![a-zA-Z\/!])/</g;
2801 $text =~ s/(?<![a-zA-Z0-9"'\/-])>/>/g;
2803 # Handle "#include <xxxxx>"
2804 $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g;
2810 #############################################################################
2811 # Function : ExpandAnnotation
2812 # Description : This turns annotations into acronym tags.
2813 # Arguments : $symbol - the symbol being documented, for error messages.
2814 # $text - the text to expand.
2815 #############################################################################
2816 sub ExpandAnnotation {
2817 my ($symbol, $param_desc) = @_;
2818 my $param_annotations = "";
2820 # look for annotations at the start of the comment part
2821 if ($param_desc =~ m%^\s*\((.*?)\):%) {
2826 @annotations = split(/\)\s*\(/,$1);
2827 foreach $annotation (@annotations) {
2828 # need to search for the longest key-match in %AnnotationDefinition
2830 my $match_annotation="";
2832 foreach $annotationdef (keys %AnnotationDefinition) {
2833 if ($annotation =~ m/^$annotationdef/) {
2834 if (length($annotationdef)>$match_length) {
2835 $match_length=length($annotationdef);
2836 $match_annotation=$annotationdef;
2840 my $annotation_extra = "";
2841 if ($match_annotation ne "") {
2842 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2843 $annotation_extra = " $1";
2845 $AnnotationsUsed{$match_annotation} = 1;
2846 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2849 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2850 "unknown annotation \"$annotation\" in documentation for $symbol.");
2851 $param_annotations .= "[$annotation]";
2855 $param_desc =~ m/^(.*?)\.*\s*$/s;
2856 $param_desc = "$1. ";
2858 if ($param_annotations ne "") {
2859 $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2861 return ($param_desc, $param_annotations);
2864 #############################################################################
2865 # Function : ExpandAbbreviations
2866 # Description : This turns the abbreviations function(), macro(), @param,
2867 # %constant, and #symbol into appropriate DocBook markup.
2868 # CDATA sections and <programlisting> parts are skipped.
2869 # Arguments : $symbol - the symbol being documented, for error messages.
2870 # $text - the text to expand.
2871 #############################################################################
2873 sub ExpandAbbreviations {
2874 my ($symbol, $text) = @_;
2876 # Note: This is a fallback and normally done in the markdown parser
2878 # Convert "|[" and "]|" into the start and end of program listing examples.
2879 # Support \[<!-- language="C" --> modifiers
2880 $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2881 $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2882 $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2884 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2886 return &ModifyXMLElements ($text, $symbol,
2887 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2888 \&ExpandAbbreviationsEndTag,
2889 \&ExpandAbbreviationsCallback);
2893 # Returns the end tag (as a regexp) corresponding to the given start tag.
2894 sub ExpandAbbreviationsEndTag {
2895 my ($start_tag) = @_;
2897 if ($start_tag eq "<!\[CDATA\[") {
2899 } elsif ($start_tag eq "<!DOCTYPE") {
2901 } elsif ($start_tag =~ m/<(\w+)/) {
2906 # Called inside or outside each CDATA or <programlisting> section.
2907 sub ExpandAbbreviationsCallback {
2908 my ($text, $symbol, $tag) = @_;
2910 if ($tag =~ m/^<programlisting/) {
2911 # Handle any embedded CDATA sections.
2912 return &ModifyXMLElements ($text, $symbol,
2914 \&ExpandAbbreviationsEndTag,
2915 \&ExpandAbbreviationsCallback2);
2916 } elsif ($tag eq "") {
2917 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2919 # We are outside any CDATA or <programlisting> sections, so we expand
2920 # any gtk-doc abbreviations.
2922 # Convert '@param()'
2923 # FIXME: we could make those also links ($symbol.$2), but that would be less
2924 # useful as the link target is a few lines up or down
2925 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2927 # Convert 'function()' or 'macro()'.
2928 # if there is abc_*_def() we don't want to make a link to _def()
2929 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2930 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2931 # handle #Object.func()
2932 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2934 # Convert '@param', but not '\@param'.
2935 $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2936 $text =~ s/\\\@/\@/g;
2938 # Convert '%constant', but not '\%constant'.
2939 # Also allow negative numbers, e.g. %-1.
2940 $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2941 $text =~ s/\\\%/\%/g;
2943 # Convert '#symbol', but not '\#symbol'.
2944 $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2951 # This is called inside a <programlisting>
2952 sub ExpandAbbreviationsCallback2 {
2953 my ($text, $symbol, $tag) = @_;
2956 # We are inside a <programlisting> but outside any CDATA sections,
2957 # so we expand any gtk-doc abbreviations.
2958 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2959 # why not just call it
2960 $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2961 } elsif ($tag eq "<![CDATA[") {
2962 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2963 $text = &ReplaceEntities ($text, $symbol);
2970 my ($symbol, $tag) = @_;;
2973 # Check for things like '#include', '#define', and skip them.
2974 if ($PreProcessorDirectives{$symbol}) {
2978 # Get rid of special suffixes ('-struct','-enum').
2979 $text =~ s/-struct$//;
2980 $text =~ s/-enum$//;
2982 # If the symbol is in the form "Object::signal", then change the symbol to
2983 # "Object-signal" and use "signal" as the text.
2984 if ($symbol =~ s/::/-/) {
2988 # If the symbol is in the form "Object:property", then change the symbol to
2989 # "Object--property" and use "property" as the text.
2990 if ($symbol =~ s/:/--/) {
2995 $text = tagify ($text, $tag);
2998 return &MakeXRef($symbol, $text);
3002 #############################################################################
3003 # Function : ModifyXMLElements
3004 # Description : Looks for given XML element tags within the text, and calls
3005 # the callback on pieces of text inside & outside those elements.
3006 # Used for special handling of text inside things like CDATA
3007 # and <programlisting>.
3008 # Arguments : $text - the text.
3009 # $symbol - the symbol currently being documented (only used for
3011 # $start_tag_regexp - the regular expression to match start tags.
3012 # e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3013 # CDATA sections or programlisting elements.
3014 # $end_tag_func - function which is passed the matched start tag
3015 # and should return the appropriate end tag string regexp.
3016 # $callback - callback called with each part of the text. It is
3017 # called with a piece of text, the symbol being
3018 # documented, and the matched start tag or "" if the text
3019 # is outside the XML elements being matched.
3020 #############################################################################
3021 sub ModifyXMLElements {
3022 my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3023 my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3026 while ($text =~ m/$start_tag_regexp/s) {
3027 $before_tag = $`; # Prematch for last successful match string
3028 $start_tag = $&; # Last successful match
3029 $text = $'; # Postmatch for last successful match string
3031 $result .= &$callback ($before_tag, $symbol, "");
3032 $result .= $start_tag;
3034 # get the matching end-tag for current tag
3035 $end_tag_regexp = &$end_tag_func ($start_tag);
3037 if ($text =~ m/$end_tag_regexp/s) {
3042 $result .= &$callback ($before_tag, $symbol, $start_tag);
3043 $result .= $end_tag;
3045 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3046 "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3047 # Just assume it is all inside the tag.
3048 $result .= &$callback ($text, $symbol, $start_tag);
3053 # Handle any remaining text outside the tags.
3054 $result .= &$callback ($text, $symbol, "");
3063 # Adds a tag around some text.
3064 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3066 my ($text, $elem) = @_;
3067 return "<" . $elem . ">" . $text . "</" . $elem . ">";
3071 #############################################################################
3072 # Function : MakeXRef
3073 # Description : This returns a cross-reference link to the given symbol.
3074 # Though it doesn't try to do this for a few standard C types
3075 # that it knows won't be in the documentation.
3076 # Arguments : $symbol - the symbol to try to create a XRef to.
3077 # $text - text text to put inside the XRef, defaults to $symbol
3078 #############################################################################
3081 my ($symbol, $text) = ($_[0], $_[1]);
3083 $symbol =~ s/^\s+//;
3084 $symbol =~ s/\s+$//;
3086 if (!defined($text)) {
3089 # Get rid of special suffixes ('-struct','-enum').
3090 $text =~ s/-struct$//;
3091 $text =~ s/-enum$//;
3094 if ($symbol =~ m/ /) {
3098 @TRACE@("Getting type link for $symbol -> $text\n");
3100 my $symbol_id = &CreateValidSGMLID ($symbol);
3101 return "<link linkend=\"$symbol_id\">$text</link>";
3105 #############################################################################
3106 # Function : MakeIndexterms
3107 # Description : This returns a indexterm elements for the given symbol
3108 # Arguments : $symbol - the symbol to create indexterms for
3109 #############################################################################
3111 sub MakeIndexterms {
3112 my ($symbol, $id) = @_;
3116 # make the index useful, by ommiting the namespace when sorting
3117 if ($NAME_SPACE ne "") {
3118 if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3119 $sortas=" sortas=\"$1\"";
3123 if (exists $Deprecated{$symbol}) {
3124 $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3125 $IndexEntriesDeprecated{$symbol}=$id;
3126 $IndexEntriesFull{$symbol}=$id;
3128 if (exists $Since{$symbol}) {
3129 my $since = $Since{$symbol};
3133 $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3135 $IndexEntriesSince{$symbol}=$id;
3136 $IndexEntriesFull{$symbol}=$id;
3139 $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3140 $IndexEntriesFull{$symbol}=$id;
3146 #############################################################################
3147 # Function : MakeDeprecationNote
3148 # Description : This returns a deprecation warning for the given symbol.
3149 # Arguments : $symbol - the symbol to try to create a warning for.
3150 #############################################################################
3152 sub MakeDeprecationNote {
3153 my ($symbol) = $_[0];
3155 if (exists $Deprecated{$symbol}) {
3158 $desc .= "<warning><para><literal>$symbol</literal> ";
3160 $note = $Deprecated{$symbol};
3162 if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3163 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3165 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3167 $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3171 $note = &ConvertMarkDown($symbol, $note);
3172 $desc .= " " . $note;
3174 $desc .= "</warning>\n";
3179 #############################################################################
3180 # Function : MakeConditionDescription
3181 # Description : This returns a sumary of conditions for the given symbol.
3182 # Arguments : $symbol - the symbol to try to create the sumary.
3183 #############################################################################
3185 sub MakeConditionDescription {
3186 my ($symbol) = $_[0];
3189 if (exists $Deprecated{$symbol}) {
3194 if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3195 $desc .= "deprecated:$1";
3197 $desc .= "deprecated";
3201 if (exists $Since{$symbol}) {
3206 if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3207 $desc .= "since:$1";
3213 if (exists $StabilityLevel{$symbol}) {
3217 $desc .= "stability:".$StabilityLevel{$symbol};
3222 $cond =~ s/\"/"/g;
3223 $desc=" condition=\"".$cond."\"";
3224 @TRACE@("condition for '$symbol' = '$desc'\n");
3229 #############################################################################
3230 # Function : GetHierarchy
3231 # Description : Returns the DocBook output describing the ancestors and
3232 # immediate children of a GObject subclass. It uses the
3233 # global @Objects and @ObjectLevels arrays to walk the tree.
3235 # Arguments : $object - the GtkObject subclass.
3236 # @hierarchy - previous hierarchy
3237 #############################################################################
3240 my ($object,$hierarchy_ref) = @_;
3241 my @hierarchy = @{$hierarchy_ref};
3243 # Find object in the objects array.
3249 for ($i = 0; $i < @Objects; $i++) {
3251 if ($ObjectLevels[$i] <= $level) {
3254 elsif ($ObjectLevels[$i] == $level + 1) {
3255 push (@children, $Objects[$i]);
3258 elsif ($Objects[$i] eq $object) {
3261 $level = $ObjectLevels[$i];
3268 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3270 push (@ancestors, $object);
3271 @TRACE@("Level: $level\n");
3272 while ($level > 1) {
3274 if ($ObjectLevels[$j] < $level) {
3275 push (@ancestors, $Objects[$j]);
3276 $level = $ObjectLevels[$j];
3277 @TRACE@("Level: $level\n");
3281 # Output the ancestors, indented and with links.
3284 for ($i = $#ancestors; $i >= 0; $i--) {
3286 # Don't add a link to the current object, i.e. when i == 0.
3288 my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]);
3289 $link_text = "<link linkend=\"$ancestor_id\">$ancestors[$i]</link>";
3291 $link_text = "$ancestors[$i]";
3293 my $indented_text = ' ' x ($level * 4) . $link_text;
3294 # Check if we already have this object
3296 for ($j = 0; $j <= $#hierarchy; $j++) {
3297 if ($hierarchy[$j] eq $indented_text) {
3303 # We have a new entry, find insert position in alphabetical order
3304 my $indent = ' ' x ($level * 4);
3306 for ($j = $last_index; $j <= $#hierarchy; $j++) {
3307 if ($hierarchy[$j] !~ m/^${indent}/) {
3311 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3312 my $stripped_text = $hierarchy[$j];
3313 if ($indented_text !~ m/<link linkend/) {
3314 $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3315 $stripped_text =~ s%</link>%%;
3317 if ($indented_text lt $stripped_text) {
3325 $last_index = 1 + $#hierarchy;
3327 splice @hierarchy, $last_index, 0, ($indented_text);
3330 # Already have this one, remmeber index as base insert point
3331 $last_index = $index + 1;
3335 # Output the children, indented and with links.
3336 for ($i = 0; $i <= $#children; $i++) {
3337 my $id = &CreateValidSGMLID ($children[$i]);
3338 my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3339 splice @hierarchy, $last_index, 0, ($indented_text);
3346 #############################################################################
3347 # Function : GetInterfaces
3348 # Description : Returns the DocBook output describing the interfaces
3349 # implemented by a class. It uses the global %Interfaces hash.
3350 # Arguments : $object - the GtkObject subclass.
3351 #############################################################################
3358 # Find object in the objects array.
3359 if (exists($Interfaces{$object})) {
3360 my @ifaces = split(' ', $Interfaces{$object});
3365 for ($i = 0; $i <= $#ifaces; $i++) {
3366 my $id = &CreateValidSGMLID ($ifaces[$i]);
3367 $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3368 if ($i < $#ifaces - 1) {
3371 elsif ($i < $#ifaces) {
3386 #############################################################################
3387 # Function : GetImplementations
3388 # Description : Returns the DocBook output describing the implementations
3389 # of an interface. It uses the global %Interfaces hash.
3390 # Arguments : $object - the GtkObject subclass.
3391 #############################################################################
3393 sub GetImplementations {
3398 foreach my $key (keys %Interfaces) {
3399 if ($Interfaces{$key} =~ /\b$object\b/) {
3400 push (@impls, $key);
3404 @impls = sort @impls;
3407 $object is implemented by
3409 for ($i = 0; $i <= $#impls; $i++) {
3410 my $id = &CreateValidSGMLID ($impls[$i]);
3411 $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3412 if ($i < $#impls - 1) {
3415 elsif ($i < $#impls) {
3430 #############################################################################
3431 # Function : GetPrerequisites
3432 # Description : Returns the DocBook output describing the prerequisites
3433 # of an interface. It uses the global %Prerequisites hash.
3434 # Arguments : $iface - the interface.
3435 #############################################################################
3437 sub GetPrerequisites {
3442 if (exists($Prerequisites{$iface})) {
3447 my @prereqs = split(' ', $Prerequisites{$iface});
3448 for ($i = 0; $i <= $#prereqs; $i++) {
3449 my $id = &CreateValidSGMLID ($prereqs[$i]);
3450 $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3451 if ($i < $#prereqs - 1) {
3454 elsif ($i < $#prereqs) {
3468 #############################################################################
3469 # Function : GetDerived
3470 # Description : Returns the DocBook output describing the derived interfaces
3471 # of an interface. It uses the global %Prerequisites hash.
3472 # Arguments : $iface - the interface.
3473 #############################################################################
3481 foreach my $key (keys %Prerequisites) {
3482 if ($Prerequisites{$key} =~ /\b$iface\b/) {
3483 push (@derived, $key);
3486 if ($#derived >= 0) {
3487 @derived = sort @derived;
3490 $iface is required by
3492 for ($i = 0; $i <= $#derived; $i++) {
3493 my $id = &CreateValidSGMLID ($derived[$i]);
3494 $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3495 if ($i < $#derived - 1) {
3498 elsif ($i < $#derived) {
3513 #############################################################################
3514 # Function : GetSignals
3515 # Description : Returns the synopsis and detailed description DocBook output
3516 # for the signal handlers of a given GtkObject subclass.
3517 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3518 #############################################################################
3526 for ($i = 0; $i <= $#SignalObjects; $i++) {
3527 if ($SignalObjects[$i] eq $object) {
3528 @TRACE@("Found signal: $SignalNames[$i]\n");
3529 my $name = $SignalNames[$i];
3530 my $symbol = "${object}::${name}";
3531 my $id = &CreateValidSGMLID ("$object-$name");
3533 $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3534 $desc .= MakeIndexterms($symbol, $id);
3536 $desc .= OutputSymbolExtraLinks($symbol);
3538 $desc .= "<programlisting language=\"C\">";
3540 $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3541 my $type_modifier = defined($1) ? $1 : "";
3544 my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3546 my $ret_type_output = "$type_modifier$xref$pointer";
3547 my $callback_name = "user_function";
3548 $desc .= "${ret_type_output}\n${callback_name} (";
3550 my $indentation = ' ' x (length($callback_name) + 2);
3551 my $pad = $indentation;
3553 my $sourceparams = $SourceSymbolParams{$symbol};
3554 my @params = split ("\n", $SignalPrototypes[$i]);
3557 my $type_len = length("gpointer");
3558 my $name_len = length("user_data");
3559 # do two passes, the first one is to calculate padding
3560 for ($l = 0; $l < 2; $l++) {
3561 for ($j = 0; $j <= $#params; $j++) {
3563 # allow alphanumerics, '_', '[' & ']' in param names
3564 if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3567 if (defined($sourceparams)) {
3568 $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3573 if (!defined($param_name)) {
3574 $param_name = "arg$j";
3577 if (length($type) + length($pointer) > $type_len) {
3578 $type_len = length($type) + length($pointer);
3580 if (length($param_name) > $name_len) {
3581 $name_len = length($param_name);
3585 $xref = &MakeXRef ($type, &tagify($type, "type"));
3586 $pad = ' ' x ($type_len - length($type) - length($pointer));
3587 $desc .= "$xref$pad $pointer${param_name},\n";
3588 $desc .= $indentation;
3591 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3592 "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3596 $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3597 $pad = ' ' x ($type_len - length("gpointer"));
3598 $desc .= "$xref$pad user_data)";
3599 $desc .= "</programlisting>\n";
3601 my $flags = $SignalFlags[$i];
3602 my $flags_string = "";
3604 if (defined ($flags)) {
3605 if ($flags =~ m/f/) {
3606 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3608 elsif ($flags =~ m/l/) {
3609 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3611 elsif ($flags =~ m/c/) {
3612 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3613 $flags_string = "Cleanup";
3615 if ($flags =~ m/r/) {
3616 if ($flags_string) { $flags_string .= " / "; }
3617 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3619 if ($flags =~ m/d/) {
3620 if ($flags_string) { $flags_string .= " / "; }
3621 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3623 if ($flags =~ m/a/) {
3624 if ($flags_string) { $flags_string .= " / "; }
3625 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3627 if ($flags =~ m/h/) {
3628 if ($flags_string) { $flags_string .= " / "; }
3629 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3633 $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";
3635 my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3637 $AllSymbols{$symbol} = 1;
3638 if (defined ($SymbolDocs{$symbol})) {
3639 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3641 $desc .= $symbol_docs;
3643 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3644 $AllDocumentedSymbols{$symbol} = 1;
3647 $desc .= &MakeDeprecationNote($symbol);
3649 $desc .= $parameters;
3650 if ($flags_string) {
3651 $desc .= "<para>Flags: $flags_string</para>\n";
3653 $desc .= OutputSymbolTraits ($symbol);
3654 $desc .= "</refsect2>";
3657 return ($synop, $desc);
3661 #############################################################################
3662 # Function : GetArgs
3663 # Description : Returns the synopsis and detailed description DocBook output
3664 # for the Args of a given GtkObject subclass.
3665 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3666 #############################################################################
3672 my $child_synop = "";
3673 my $child_desc = "";
3674 my $style_synop = "";
3675 my $style_desc = "";
3678 for ($i = 0; $i <= $#ArgObjects; $i++) {
3679 if ($ArgObjects[$i] eq $object) {
3680 @TRACE@("Found arg: $ArgNames[$i]\n");
3681 my $name = $ArgNames[$i];
3682 my $flags = $ArgFlags[$i];
3683 my $flags_string = "";
3687 if ($flags =~ m/c/) {
3688 $kind = "child property";
3691 elsif ($flags =~ m/s/) {
3692 $kind = "style property";
3699 # Remember only one colon so we don't clash with signals.
3700 my $symbol = "${object}:${name}";
3701 # use two dashes and ev. an extra separator here for the same reason.
3702 my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3704 my $type = $ArgTypes[$i];
3706 my $range = $ArgRanges[$i];
3707 my $range_output = CreateValidSGML ($range);
3708 my $default = $ArgDefaults[$i];
3709 my $default_output = CreateValidSGML ($default);
3711 if ($type eq "GtkString") {
3712 $type = "char *";
3714 if ($type eq "GtkSignal") {
3715 $type = "GtkSignalFunc, gpointer";
3716 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3717 . &MakeXRef ("gpointer");
3718 } elsif ($type =~ m/^(\w+)\*$/) {
3719 $type_output = &MakeXRef ($1, &tagify($1, "type")) . " *";
3721 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3724 if ($flags =~ m/r/) {
3725 $flags_string = "Read";
3727 if ($flags =~ m/w/) {
3728 if ($flags_string) { $flags_string .= " / "; }
3729 $flags_string .= "Write";
3731 if ($flags =~ m/x/) {
3732 if ($flags_string) { $flags_string .= " / "; }
3733 $flags_string .= "Construct";
3735 if ($flags =~ m/X/) {
3736 if ($flags_string) { $flags_string .= " / "; }
3737 $flags_string .= "Construct Only";
3740 $AllSymbols{$symbol} = 1;
3742 if (defined($SymbolDocs{$symbol}) &&
3743 !IsEmptyDoc($SymbolDocs{$symbol})) {
3744 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3745 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3746 $AllDocumentedSymbols{$symbol} = 1;
3749 if (!($ArgBlurbs[$i] eq "")) {
3750 $AllDocumentedSymbols{$symbol} = 1;
3752 # FIXME: print a warning?
3753 @TRACE@(".. no description\n");
3755 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3758 my $pad1 = " " x (24 - length ($name));
3760 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";
3761 my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3762 $arg_desc .= MakeIndexterms($symbol, $id);
3764 $arg_desc .= OutputSymbolExtraLinks($symbol);
3766 $arg_desc .= "<programlisting> “$name”$pad1 $type_output</programlisting>\n";
3767 $arg_desc .= $blurb;
3768 $arg_desc .= &MakeDeprecationNote($symbol);
3770 if ($flags_string) {
3771 $arg_desc .= "<para>Flags: $flags_string</para>\n";
3774 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3776 if ($default ne "") {
3777 $arg_desc .= "<para>Default value: $default_output</para>\n";
3779 $arg_desc .= OutputSymbolTraits ($symbol);
3780 $arg_desc .= "</refsect2>\n";
3782 if ($flags =~ m/c/) {
3783 $child_synop .= $arg_synop;
3784 $child_desc .= $arg_desc;
3786 elsif ($flags =~ m/s/) {
3787 $style_synop .= $arg_synop;
3788 $style_desc .= $arg_desc;
3791 $synop .= $arg_synop;
3796 return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3800 #############################################################################
3801 # Function : ReadSourceDocumentation
3802 # Description : This reads in the documentation embedded in comment blocks
3803 # in the source code (for Gnome).
3805 # Parameter descriptions override any in the template files.
3806 # Function descriptions are placed before any description from
3807 # the template files.
3809 # It recursively descends the source directory looking for .c
3810 # files and scans them looking for specially-formatted comment
3813 # Arguments : $source_dir - the directory to scan.
3814 #############m###############################################################
3816 sub ReadSourceDocumentation {
3817 my ($source_dir) = @_;
3818 my ($file, $dir, @suffix_list, $suffix);
3820 # prepend entries from @SOURCE_DIR
3821 for my $dir (@SOURCE_DIRS) {
3822 # Check if the filename is in the ignore list.
3823 if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3824 @TRACE@("Skipping source directory: $source_dir");
3829 @TRACE@("Scanning source directory: $source_dir");
3831 # This array holds any subdirectories found.
3834 @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3836 opendir (SRCDIR, $source_dir)
3837 || die "Can't open source directory $source_dir: $!";
3839 foreach $file (readdir (SRCDIR)) {
3840 if ($file =~ /^\./) {
3842 } elsif (-d "$source_dir/$file") {
3843 push (@subdirs, $file);
3844 } elsif (@suffix_list) {
3845 foreach $suffix (@suffix_list) {
3846 if ($file =~ m/\.\Q${suffix}\E$/) {
3847 &ScanSourceFile ("$source_dir/$file");
3850 } elsif ($file =~ m/\.[ch]$/) {
3851 &ScanSourceFile ("$source_dir/$file");
3856 # Now recursively scan the subdirectories.
3857 foreach $dir (@subdirs) {
3858 &ReadSourceDocumentation ("$source_dir/$dir");
3863 #############################################################################
3864 # Function : ScanSourceFile
3865 # Description : Scans one source file looking for specially-formatted comment
3866 # blocks. Later &MergeSourceDocumentation is used to merge any
3867 # documentation found with the documentation already read in
3868 # from the template files.
3870 # Arguments : $file - the file to scan.
3871 #############################################################################
3873 sub ScanSourceFile {
3877 # prepend entries from @SOURCE_DIR
3878 for my $dir (@SOURCE_DIRS) {
3879 # Check if the filename is in the ignore list.
3880 if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3881 @TRACE@("Skipping source file: $file");
3886 if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3889 &LogWarning ($file, 1, "Can't find basename for this filename.");
3893 # Check if the basename is in the list of files to ignore.
3894 if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3895 @TRACE@("Skipping source file: $file");
3899 @TRACE@("Scanning source file: $file");
3901 open (SRCFILE, $file)
3902 || die "Can't open $file: $!";
3903 my $in_comment_block = 0;
3906 my ($description, $return_desc);
3907 my ($since_desc, $stability_desc, $deprecated_desc);
3911 # Look for the start of a comment block.
3912 if (!$in_comment_block) {
3913 if (m%^\s*/\*.*\*/%) {
3914 #one-line comment - not gtkdoc
3915 } elsif (m%^\s*/\*\*\s%) {
3916 @TRACE@("Found comment block start\n");
3918 $in_comment_block = 1;
3920 # Reset all the symbol data.
3926 $deprecated_desc = "";
3927 $stability_desc = "";
3928 $current_param = -1;
3934 # We're in a comment block. Check if we've found the end of it.
3937 # maybe its not even meant to be a gtk-doc comment?
3938 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3940 # Add the return value description onto the end of the params.
3942 # TODO(ensonic): check for duplicated Return docs
3943 # &LogWarning ($file, $., "Multiple Returns for $symbol.");
3944 push (@params, "Returns");
3945 push (@params, $return_desc);
3947 # Convert special SGML characters
3948 $description = &ConvertSGMLChars ($symbol, $description);
3950 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3951 $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3954 # Handle Section docs
3955 if ($symbol =~ m/SECTION:\s*(.*)/) {
3959 if (scalar %KnownSymbols) {
3960 if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
3961 &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
3965 @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
3966 for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3967 @TRACE@(" '".$params[$k]."'\n");
3968 $params[$k] = "\L$params[$k]";
3970 if ($params[$k] eq "short_description") {
3971 $key = "$TMPL_DIR/$real_symbol:Short_Description";
3972 } elsif ($params[$k] eq "see_also") {
3973 $key = "$TMPL_DIR/$real_symbol:See_Also";
3974 } elsif ($params[$k] eq "title") {
3975 $key = "$TMPL_DIR/$real_symbol:Title";
3976 } elsif ($params[$k] eq "stability") {
3977 $key = "$TMPL_DIR/$real_symbol:Stability_Level";
3978 } elsif ($params[$k] eq "section_id") {
3979 $key = "$TMPL_DIR/$real_symbol:Section_Id";
3980 } elsif ($params[$k] eq "include") {
3981 $key = "$TMPL_DIR/$real_symbol:Include";
3982 } elsif ($params[$k] eq "image") {
3983 $key = "$TMPL_DIR/$real_symbol:Image";
3985 if (defined($key)) {
3986 $SourceSymbolDocs{$key}=$params[$k+1];
3987 $SourceSymbolSourceFile{$key} = $file;
3988 $SourceSymbolSourceLine{$key} = $.;
3991 $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
3992 $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
3993 $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
3994 #$SourceSymbolTypes{$symbol} = "SECTION";
3996 @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
3997 $SourceSymbolDocs{$symbol} = $description;
3998 $SourceSymbolParams{$symbol} = [ @params ];
3999 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4000 #if (defined $DeclarationTypes{$symbol}) {
4001 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4003 $SourceSymbolSourceFile{$symbol} = $file;
4004 $SourceSymbolSourceLine{$symbol} = $.;
4008 ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4009 $since_desc =~ s/^\s+//;
4010 $since_desc =~ s/\s+$//;
4011 @TRACE@("Since($symbol) : [$since_desc]\n");
4012 $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4013 if(scalar @extra_lines) {
4014 &LogWarning ($file, $., "multi-line since docs found");
4018 if ($stability_desc) {
4019 $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4020 $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4023 if ($deprecated_desc) {
4024 if (!exists $Deprecated{$symbol}) {
4025 # don't warn for signals and properties
4026 #if ($symbol !~ m/::?(.*)/) {
4027 if (defined $DeclarationTypes{$symbol}) {
4028 &LogWarning ($file, $.,
4029 "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4030 " (See the --deprecated-guards option for gtkdoc-scan.)");
4033 $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4037 $in_comment_block = 0;
4041 # Get rid of ' * ' at start of every line in the comment block.
4043 # But make sure we don't get rid of the newline at the end.
4047 @TRACE@("scanning :$_");
4049 # If we haven't found the symbol name yet, look for it.
4051 if (m%^\s*(SECTION:\s*\S+)%) {
4053 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4054 } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-a-z0-9_ ]+\)\s*)*$%) {
4056 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4061 if ($in_part eq "description") {
4062 # Get rid of 'Description:'
4063 s%^\s*Description:%%;
4066 if (m%^\s*(returns|return\s+value):%i) {
4067 # we're in param section and have not seen the blank line
4068 if($in_part ne "") {
4070 $in_part = "return";
4073 } elsif (m%^\s*since:%i) {
4074 # we're in param section and have not seen the blank line
4075 if($in_part ne "") {
4080 } elsif (m%^\s*deprecated:%i) {
4081 # we're in param section and have not seen the blank line
4082 if($in_part ne "") {
4083 $deprecated_desc = $';
4084 $in_part = "deprecated";
4087 } elsif (m%^\s*stability:%i) {
4088 $stability_desc = $';
4089 $in_part = "stability";
4093 if ($in_part eq "description") {
4096 } elsif ($in_part eq "return") {
4099 } elsif ($in_part eq "since") {
4102 } elsif ($in_part eq "stability") {
4103 $stability_desc .= $_;
4105 } elsif ($in_part eq "deprecated") {
4106 $deprecated_desc .= $_;
4110 # We must be in the parameters. Check for the empty line below them.
4112 $in_part = "description";
4116 # Look for a parameter name.
4117 if (m%^\s*@(\S+)\s*:\s*%) {
4118 my $param_name = $1;
4119 my $param_desc = $';
4121 @TRACE@("Found parameter: $param_name\n");
4122 # Allow varargs variations
4123 if ($param_name =~ m/^\.\.\.$/) {
4124 $param_name = "...";
4126 @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4128 push (@params, $param_name);
4129 push (@params, $param_desc);
4130 $current_param += $PARAM_FIELD_COUNT;
4134 # We must be in the middle of a parameter description, so add it on
4135 # to the last element in @params.
4136 if ($current_param == -1) {
4137 &LogWarning ($file, $., "Parsing comment block file : parameter expected.");
4139 $params[$#params] .= $_;
4145 #############################################################################
4146 # Function : OutputMissingDocumentation
4147 # Description : Outputs report of documentation coverage to a file
4150 #############################################################################
4152 sub OutputMissingDocumentation {
4153 my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4154 my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4156 my $n_documented = 0;
4157 my $n_incomplete = 0;
4163 my $buffer_deprecated = "";
4164 my $buffer_descriptions = "";
4166 open(UNDOCUMENTED, ">$new_undocumented_file")
4167 || die "Can't create $new_undocumented_file";
4169 foreach $symbol (sort (keys (%AllSymbols))) {
4170 # FIXME: should we print LogWarnings for undocumented stuff?
4172 #my $ssfile = &GetSymbolSourceFile($symbol);
4173 #my $ssline = &GetSymbolSourceLine($symbol);
4174 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4176 if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4178 if (exists ($AllDocumentedSymbols{$symbol})) {
4180 if (exists ($AllIncompleteSymbols{$symbol})) {
4182 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4183 #$buffer .= "\t0: ".$location;
4185 } elsif (exists $Deprecated{$symbol}) {
4186 if (exists ($AllIncompleteSymbols{$symbol})) {
4188 $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4189 #$buffer .= "\t1a: ".$location;
4191 $buffer_deprecated .= $symbol . "\n";
4192 #$buffer .= "\t1b: ".$location;
4195 if (exists ($AllIncompleteSymbols{$symbol})) {
4197 $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4198 #$buffer .= "\t2a: ".$location;
4200 $buffer .= $symbol . "\n";
4201 #$buffer .= "\t2b: ".$location;
4204 } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4206 if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4207 || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4210 # cut off the leading namespace ($TMPL_DIR)
4211 $symbol =~ m/^.*\/(.*)$/;
4212 $buffer_descriptions .= $1 . "\n";
4217 $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
4222 $percent = ($n_documented / $total) * 100.0;
4225 printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4226 print UNDOCUMENTED "$n_documented symbols documented.\n";
4227 print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4228 print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
4230 print UNDOCUMENTED $buffer;
4231 close (UNDOCUMENTED);
4233 return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4235 printf "%.0f%% symbol docs coverage", $percent;
4236 print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4237 print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4241 #############################################################################
4242 # Function : OutputUndeclaredSymbols
4243 # Description : Outputs symbols that are listed in the section file, but not
4244 # declaration is found in the sources
4247 #############################################################################
4249 sub OutputUndeclaredSymbols {
4250 my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4251 my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4253 open(UNDECLARED, ">$new_undeclared_file")
4254 || die "Can't create $new_undeclared_file";
4256 if (%UndeclaredSymbols) {
4257 print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4258 print UNDECLARED "\n";
4259 print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4263 return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4266 #############################################################################
4267 # Function : OutputUnusedSymbols
4268 # Description : Outputs symbols that are documented in comments, but not
4269 # declared in the sources
4272 #############################################################################
4274 sub OutputUnusedSymbols {
4276 my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4277 my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4279 open (UNUSED, ">$new_unused_file")
4280 || die "Can't open $new_unused_file";
4282 foreach $symbol (sort keys (%Declarations)) {
4283 if (!defined ($DeclarationOutput{$symbol})) {
4284 print (UNUSED "$symbol\n");
4288 foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4289 print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4293 if ($num_unused != 0) {
4294 &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4295 "They should be added to $MODULE-sections.txt in the appropriate place.");
4298 return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4302 #############################################################################
4303 # Function : OutputAllSymbols
4304 # Description : Outputs list of all symbols to a file
4307 #############################################################################
4309 sub OutputAllSymbols {
4310 my $n_documented = 0;
4316 open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4317 || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4319 foreach $symbol (sort (keys (%AllSymbols))) {
4320 print SYMBOLS $symbol . "\n";
4326 #############################################################################
4327 # Function : OutputSymbolsWithoutSince
4328 # Description : Outputs list of all symbols without a since tag to a file
4331 #############################################################################
4333 sub OutputSymbolsWithoutSince {
4334 my $n_documented = 0;
4340 open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4341 || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4343 foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4344 if (!defined $Since{$symbol}) {
4345 print SYMBOLS $symbol . "\n";
4353 #############################################################################
4354 # Function : MergeSourceDocumentation
4355 # Description : This merges documentation read from a source file into the
4356 # documentation read in from a template file.
4358 # Parameter descriptions override any in the template files.
4359 # Function descriptions are placed before any description from
4360 # the template files.
4363 #############################################################################
4365 sub MergeSourceDocumentation {
4369 if (scalar %SymbolDocs) {
4370 @Symbols=keys (%SymbolDocs);
4371 @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4374 # filter scanned declarations, with what we suppress from -sections.txt
4376 foreach $symbol (keys (%Declarations)) {
4377 if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4381 # , add the rest from -sections.txt
4382 foreach $symbol (keys (%KnownSymbols)) {
4383 if ($KnownSymbols{$symbol} == 1) {
4387 # and add whats found in the source
4388 foreach $symbol (keys (%SourceSymbolDocs)) {
4391 @Symbols = keys (%tmp);
4392 @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4394 foreach $symbol (@Symbols) {
4395 $AllSymbols{$symbol} = 1;
4397 my $have_tmpl_docs = 0;
4399 ## see if the symbol is documented in template
4400 my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4401 my $check_tmpl_doc =$tmpl_doc;
4402 # remove all xml-tags and whitespaces
4403 $check_tmpl_doc =~ s/<.*?>//g;
4404 $check_tmpl_doc =~ s/\s//g;
4406 if ($check_tmpl_doc ne "") {
4407 $have_tmpl_docs = 1;
4409 # if the docs have just an empty para, don't merge that.
4410 $check_tmpl_doc = $tmpl_doc;
4411 $check_tmpl_doc =~ s/(\s|\n)//msg;
4412 if ($check_tmpl_doc eq "<para></para>") {
4417 if (exists ($SourceSymbolDocs{$symbol})) {
4418 my $type = $DeclarationTypes {$symbol};
4420 @TRACE@("merging [$symbol] from source\n");
4422 my $item = "Parameter";
4423 if (defined ($type)) {
4424 if ($type eq 'STRUCT') {
4426 } elsif ($type eq 'ENUM') {
4428 } elsif ($type eq 'UNION') {
4435 my $src_doc = $SourceSymbolDocs{$symbol};
4436 # remove leading and training whitespaces
4437 $src_doc =~ s/^\s+//;
4438 $src_doc =~ s/\s+$//;
4440 # Don't output warnings for overridden titles as titles are
4441 # automatically generated in the -sections.txt file, and thus they
4442 # are often overridden.
4443 if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4444 # check if content is different
4445 if ($tmpl_doc ne $src_doc) {
4446 #print "[$tmpl_doc] [$src_doc]\n";
4447 &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4448 "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4452 if ($src_doc ne "") {
4453 $AllDocumentedSymbols{$symbol} = 1;
4456 # Do not add <para> to nothing, it breaks missing docs checks.
4457 my $src_doc_para = "";
4458 if ($src_doc ne "") {
4459 $src_doc_para = $src_doc;
4462 if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4463 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4464 } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4465 # For the title/summary/see also section docs we don't want to
4466 # add any <para> tags.
4467 $SymbolDocs{$symbol} = "$src_doc"
4469 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4473 if ($symbol =~ m/.*::.*/) {
4474 # For signals we prefer the param names from the source docs,
4475 # since the ones from the templates are likely to contain the
4476 # artificial argn names which are generated by gtkdoc-scangobj.
4477 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4478 # FIXME: we need to check for empty docs here as well!
4480 # The templates contain the definitive parameter names and order,
4481 # so we will not change that. We only override the actual text.
4482 my $tmpl_params = $SymbolParams{$symbol};
4483 if (!defined ($tmpl_params)) {
4484 @TRACE@("No merge needed for $symbol\n");
4485 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4486 # FIXME: we still like to get the number of params and merge
4487 # 1) we would noticed that params have been removed/renamed
4488 # 2) we would catch undocumented params
4489 # params are not (yet) exported in -decl.txt so that we
4490 # could easily grab them :/
4492 my $params = $SourceSymbolParams{$symbol};
4494 @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4495 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4496 my $tmpl_param_name = $$tmpl_params[$j];
4498 # Try to find the param in the source comment documentation.
4501 @TRACE@(" try merge param $tmpl_param_name\n");
4502 for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4503 my $param_name = $$params[$k];
4504 my $param_desc = $$params[$k + 1];
4506 @TRACE@(" test param $param_name\n");
4507 # We accept changes in case, since the Gnome source
4508 # docs contain a lot of these.
4509 if ("\L$param_name" eq "\L$tmpl_param_name") {
4512 # Override the description.
4513 $$tmpl_params[$j + 1] = $param_desc;
4515 # Set the name to "" to mark it as used.
4521 # If it looks like the parameters are there, but not
4522 # in the right place, try to explain a bit better.
4523 if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4524 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4525 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4529 # Now we output a warning if parameters have been described which
4531 for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4532 my $param_name = $$params[$j];
4534 # the template builder cannot detect if a macro returns
4536 if(($type eq "MACRO") && ($param_name eq "Returns")) {
4537 # FIXME: do we need to add it then to tmpl_params[] ?
4538 my $num=$#$tmpl_params;
4539 @TRACE@(" adding Returns: to macro docs for $symbol.\n");
4540 $$tmpl_params[$num+1]="Returns";
4541 $$tmpl_params[$num+2]=$$params[$j+1];
4544 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4545 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4551 if ($have_tmpl_docs) {
4552 $AllDocumentedSymbols{$symbol} = 1;
4553 @TRACE@("merging [$symbol] from template\n");
4556 @TRACE@("[$symbol] undocumented\n");
4560 # if this symbol is documented, check if docs are complete
4561 $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4562 # remove all xml-tags and whitespaces
4563 $check_tmpl_doc =~ s/<.*?>//g;
4564 $check_tmpl_doc =~ s/\s//g;
4565 if ($check_tmpl_doc ne "") {
4566 my $tmpl_params = $SymbolParams{$symbol};
4567 if (defined ($tmpl_params)) {
4568 my $type = $DeclarationTypes {$symbol};
4570 my $item = "Parameter";
4571 if (defined ($type)) {
4572 if ($type eq 'STRUCT') {
4574 } elsif ($type eq 'ENUM') {
4576 } elsif ($type eq 'UNION') {
4583 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4585 if ($#$tmpl_params > 0) {
4587 for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4588 # Output a warning if the parameter is empty and
4589 # remember for stats.
4590 my $tmpl_param_name = $$tmpl_params[$j];
4591 my $tmpl_param_desc = $$tmpl_params[$j + 1];
4592 if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4593 if (exists ($AllIncompleteSymbols{$symbol})) {
4594 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4596 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4598 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4599 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4604 if ($#$tmpl_params == 0) {
4605 $AllIncompleteSymbols{$symbol}="<items>";
4606 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4607 "$item descriptions for $symbol are missing in source code comment block.");
4609 # $#$tmpl_params==-1 means we don't know about parameters
4610 # this unfortunately does not tell if there should be some
4615 @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4618 #############################################################################
4619 # Function : IsEmptyDoc
4620 # Description : Check if a doc-string is empty. Its also regarded as empty if
4621 # it only consist of whitespace or e.g. FIXME.
4622 # Arguments : the doc-string
4623 #############################################################################
4627 if ($doc =~ /^\s*$/) {
4631 if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4638 #############################################################################
4639 # Function : ConvertMarkDown
4640 # Description : Converts mark down syntax to the respective docbook.
4641 # http://de.wikipedia.org/wiki/Markdown
4642 # Inspired by the design of ParseDown
4643 # http://parsedown.org/
4644 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4645 # Arguments : the symbol name, the doc-string
4646 #############################################################################
4648 sub ConvertMarkDown {
4649 my ($symbol, $text) = @_;
4651 $text = &MarkDownParse ($text, $symbol);
4656 # SUPPORTED MARKDOWN
4657 # ==================
4666 # Setext-style Headers
4667 # --------------------
4675 # Ordered (unnested) Lists
4676 # ------------------------
4680 # 1. item 2 with loooong
4685 # Note: we require a blank line above the list items
4688 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4690 sub MarkDownParseBlocks {
4691 my ($linesref, $symbol, $context) = @_;
4694 my $md_block = { type => "" };
4696 OUTER: foreach $line (@$linesref) {
4697 my $first_char = substr ($line, 0, 1);
4698 my $deindented_line;
4700 if ($md_block->{"type"} eq "markup") {
4701 if (!$md_block->{"closed"}) {
4702 if (index ($line, $md_block->{"start"}) != -1) {
4703 $md_block->{"depth"}++;
4705 if (index ($line, $md_block->{"end"}) != -1) {
4706 if ($md_block->{"depth"} > 0) {
4707 $md_block->{"depth"}--;
4709 $md_block->{"closed"} = 1;
4712 $md_block->{"text"} .= "\n" . $line;
4717 $deindented_line = $line;
4718 $deindented_line =~ s/^\s+//;
4720 if ($md_block->{"type"} eq "heading") {
4721 # a heading is ended by any level less than or equal
4722 if ($md_block->{"level"} == 1) {
4723 if ($line =~ /^={4,}[ \t]*$/) {
4724 my $text = pop @{$md_block->{"lines"}};
4725 $md_block->{"interrupted"} = 0;
4726 push @md_blocks, $md_block;
4728 $md_block = { type => "heading",
4733 } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4734 $md_block->{"interrupted"} = 0;
4735 push @md_blocks, $md_block;
4737 $md_block = { type => "heading",
4744 # push lines into the block until the end is reached
4745 push @{$md_block->{"lines"}}, $line;
4749 if ($line =~ /^[=]{4,}[ \t]*$/) {
4750 my $text = pop @{$md_block->{"lines"}};
4751 $md_block->{"interrupted"} = 0;
4752 push @md_blocks, $md_block;
4754 $md_block = { type => "heading",
4759 } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4760 my $text = pop @{$md_block->{"lines"}};
4761 $md_block->{"interrupted"} = 0;
4762 push @md_blocks, $md_block;
4764 $md_block = { type => "heading",
4769 } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4770 $md_block->{"interrupted"} = 0;
4771 push @md_blocks, $md_block;
4773 $md_block = { type => "heading",
4777 level => length($1) };
4780 # push lines into the block until the end is reached
4781 push @{$md_block->{"lines"}}, $line;
4785 } elsif ($md_block->{"type"} eq "code") {
4786 if ($line =~ /^[ \t]*\]\|/) {
4787 push @md_blocks, $md_block;
4788 $md_block = { type => "paragraph",
4792 push @{$md_block->{"lines"}}, $line;
4797 if ($deindented_line eq "") {
4798 $md_block->{"interrupted"} = 1;
4802 if ($md_block->{"type"} eq "quote") {
4803 if (!$md_block->{"interrupted"}) {
4804 $line =~ s/^[ ]*>[ ]?//;
4805 push @{$md_block->{"lines"}}, $line;
4808 } elsif ($md_block->{"type"} eq "li") {
4809 my $marker = $md_block->{"marker"};
4810 if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4811 my $indentation = $1;
4812 if ($md_block->{"indentation"} ne $indentation) {
4813 push @{$md_block->{"lines"}}, $line;
4816 my $ordered = $md_block->{"ordered"};
4817 $lines =~ s/^[ ]{0,4}//;
4818 $md_block->{"last"} = 0;
4819 push @md_blocks, $md_block;
4820 $md_block = { type => "li",
4821 ordered => $ordered,
4822 indentation => $indentation,
4826 lines => [ $lines ] };
4831 if ($md_block->{"interrupted"}) {
4832 if ($first_char eq " ") {
4833 push @{$md_block->{"lines"}}, "";
4834 $line =~ s/^[ ]{0,4}//;
4835 push @{$md_block->{"lines"}}, $line;
4836 $md_block->{"interrupted"} = 0;
4840 $line =~ s/^[ ]{0,4}//;
4841 push @{$md_block->{"lines"}}, $line;
4846 # indentation sensitive types
4848 if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4850 push @md_blocks, $md_block;
4852 $md_block = { type => "heading",
4856 level => length($1) };
4859 } elsif ($line =~ /^={4,}[ \t]*$/) {
4860 # setext heading (====)
4862 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4863 push @md_blocks, $md_block;
4864 $md_block->{"type"} = "heading";
4865 $md_block->{"lines"} = [];
4866 $md_block->{"level"} = 1;
4870 } elsif ($line =~ /^-{4,}[ \t]*$/) {
4871 # setext heading (-----)
4873 if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4874 push @md_blocks, $md_block;
4875 $md_block->{"type"} = "heading";
4876 $md_block->{"lines"} = [];
4877 $md_block->{"level"} = 2;
4881 } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4883 $md_block->{"interrupted"} = 1;
4884 push @md_blocks, $md_block;
4885 $md_block = { type => "code",
4891 # indentation insensitive types
4892 if ($line =~ /^[ ]*<!DOCTYPE/) {
4893 push @md_blocks, $md_block;
4895 $md_block = { type => "markup",
4896 text => $deindented_line,
4902 } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4903 # markup, including <?xml version="1.0"?>
4905 my $is_self_closing = defined($2);
4906 # FIXME: why do we need to skip https? here, if we generalize this to all
4907 # uri schemes we get parsing errors
4908 if (! $MD_TEXT_LEVEL_ELEMENTS{$tag} && $tag !~ /^https?/) {
4909 push @md_blocks, $md_block;
4911 if ($is_self_closing) {
4912 $md_block = { type => "self-closing tag",
4913 text => $deindented_line };
4914 $is_self_closing = 0;
4918 $md_block = { type => "markup",
4919 text => $deindented_line,
4920 start => "<" . $tag . ">",
4921 end => "</" . $tag . ">",
4924 if ($deindented_line =~ /<\/$tag>/) {
4925 $md_block->{"closed"} = 1;
4929 } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
4931 push @md_blocks, $md_block;
4933 my $indentation = $1;
4934 $lines =~ s/^[ ]{0,4}//;
4935 $md_block = { type => "li",
4937 indentation => $indentation,
4941 lines => [ $lines ] };
4943 } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
4944 push @md_blocks, $md_block;
4945 $md_block = { type => "quote",
4952 if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
4953 push @md_blocks, $md_block;
4955 my $indentation = $1;
4956 $lines =~ s/^[ ]{0,4}//;
4958 $md_block = { type => "li",
4960 indentation => $indentation,
4961 marker => "\\d+[.]",
4964 lines => [ $lines ] };
4970 if ($md_block->{"type"} eq "paragraph") {
4971 if ($md_block->{"interrupted"}) {
4972 push @md_blocks, $md_block;
4973 $md_block = { type => "paragraph",
4977 $md_block->{"text"} .= "\n" . $line;
4980 push @md_blocks, $md_block;
4981 $md_block = { type => "paragraph",
4986 push @md_blocks, $md_block;
4993 sub MarkDownParseSpanElementsInner {
4994 my ($text, $markersref) = @_;
4996 my %markers = map { $_ => 1 } @$markersref;
4998 while ($text ne "") {
4999 my $closest_marker = "";
5000 my $closest_marker_index = 0;
5001 my $closest_marker_position = -1;
5002 my $text_marker = "";
5009 while ( ($marker, $use) = each %markers ) {
5010 my $marker_position;
5016 $marker_position = index ($text, $marker);
5018 if ($marker_position < 0) {
5019 $markers{$marker} = 0;
5023 if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5024 $closest_marker = $marker;
5025 $closest_marker_index = $i;
5026 $closest_marker_position = $marker_position;
5030 if ($closest_marker_position >= 0) {
5031 $text_marker = substr ($text, $closest_marker_position);
5034 if ($text_marker eq "") {
5040 $markup .= substr ($text, 0, $closest_marker_position);
5041 $text = substr ($text, $closest_marker_position);
5042 @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5044 if ($closest_marker eq "![" || $closest_marker eq "[") {
5047 if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5050 %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5053 $offset = length ($&);
5054 if ($element{"!"}) {
5058 $remaining_text = substr ($text, $offset);
5059 if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5064 $offset += length ($&);
5065 } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5066 $element{"ref"} = $1;
5067 $offset += length ($&);
5074 if ($element{"»"}) {
5075 $element{"»"} =~ s/&/&/g;
5076 $element{"»"} =~ s/</</g;
5078 if ($element{"!"}) {
5079 $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5081 if (defined ($element{"a"})) {
5082 $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5085 $markup .= "</inlinemediaobject>";
5086 } elsif ($element{"ref"}) {
5087 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5088 $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5090 if (defined ($element{"#"})) {
5091 # title attribute not supported
5094 $markup .= ">" . $element{"a"} . "</link>";
5096 $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5097 $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5099 if (defined ($element{"#"})) {
5100 # title attribute not supported
5103 $markup .= ">" . $element{"a"} . "</ulink>";
5106 $markup .= $closest_marker;
5107 if ($closest_marker eq "![") {
5113 } elsif ($closest_marker eq "<") {
5114 if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5115 my $element_url = $1;
5116 $element_url =~ s/&/&/g;
5117 $element_url =~ s/</</g;
5119 $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5120 $offset = length ($&);
5121 } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5122 $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5123 $offset = length ($&);
5124 } elsif ($text =~ /^<[^>]+?>/) {
5126 $offset = length ($&);
5131 } elsif ($closest_marker eq "\\") {
5132 my $special_char = substr ($text, 1, 1);
5133 if ($MD_ESCAPABLE_CHARS{$special_char} ||
5134 $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5135 $markup .= $special_char;
5141 } elsif ($closest_marker eq "`") {
5142 if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5143 my $element_text = $2;
5144 $markup .= "<literal>" . $element_text . "</literal>";
5145 $offset = length ($&);
5150 } elsif ($closest_marker eq "@") {
5151 # Convert '@param()'
5152 # FIXME: we could make those also links ($symbol.$2), but that would be less
5153 # useful as the link target is a few lines up or down
5154 if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5155 $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5156 $offset = length ($&);
5157 } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5158 # Convert '@param', but not '\@param'.
5159 $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5160 $offset = length ($&);
5161 } elsif ($text =~ /^\\\@/) {
5163 $offset = length ($&);
5168 } elsif ($closest_marker eq "#") {
5169 if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5170 # handle #Object.func()
5171 $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5172 $offset = length ($&);
5173 } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5174 # Convert '#symbol', but not '\#symbol'.
5175 $markup .= $1 . &MakeHashXRef ($2, "type");
5176 $offset = length ($&);
5177 } elsif ($text =~ /^\\#/) {
5179 $offset = length ($&);
5184 } elsif ($closest_marker eq "%") {
5185 if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5186 # Convert '%constant', but not '\%constant'.
5187 # Also allow negative numbers, e.g. %-1.
5188 $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5189 $offset = length ($&);
5190 } elsif ($text =~ /^\\%/) {
5192 $offset = length ($&);
5200 $text = substr ($text, $offset);
5207 sub MarkDownParseSpanElements {
5209 my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5211 $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5213 # Convert 'function()' or 'macro()'.
5214 # if there is abc_*_def() we don't want to make a link to _def()
5215 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5216 $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5221 sub ReplaceEntities {
5222 my ($text, $symbol) = @_;
5224 my @entities = ( [ "<", "<" ],
5233 [ "&", "&" ] ); # Do this last, or the others get messed up.
5236 # Expand entities in <programlisting> even inside CDATA since
5237 # we changed the definition of |[ to add CDATA
5238 for ($i = 0; $i <= $#entities; $i++) {
5239 if ($text =~ s/$entities[$i][0]/$entities[$i][1]/g) {
5240 # don't warn about * since it is expected to be present
5241 # for C-style comments
5242 if ($entities[$i][0] ne "*") {
5243 $warn .= "$entities[$i][0] ";
5250 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
5251 "Deprecated entities found in documentation for $symbol: $warn");
5257 sub MarkDownOutputDocBook {
5258 my ($blocksref, $symbol, $context) = @_;
5261 my @blocks = @$blocksref;
5263 foreach $block (@blocks) {
5267 if ($block->{"type"} eq "paragraph") {
5268 $text = &MarkDownParseSpanElements ($block->{"text"});
5269 if ($context eq "li" && $output eq "") {
5270 if ($block->{"interrupted"}) {
5271 $output .= "\n"."<para>".$text."</para>"."\n";
5273 $output .= "<para>".$text."</para>";
5279 $output .= "<para>".$text."</para>"."\n";
5282 } elsif ($block->{"type"} eq "heading") {
5285 $title = &MarkDownParseSpanElements ($block->{"text"});
5287 if ($block->{"level"} == 1) {
5293 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5294 if (defined ($block->{"id"})) {
5295 $output .= "<" . $tag . " id=\"" . $block->{"id"} . "\">";
5297 $output .= "<" . $tag . ">";
5300 $output .= "<title>" . $title . "</title>" . $text . "</" . $tag . ">\n";
5301 } elsif ($block->{"type"} eq "li") {
5302 my $tag = "itemizedlist";
5304 if ($block->{"first"}) {
5305 if ($block->{"ordered"}) {
5306 $tag = "orderedlist";
5308 $output .= "<".$tag.">\n";
5311 if ($block->{"interrupted"}) {
5312 push @{$block->{"lines"}}, "";
5315 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5316 $output .= "<listitem>".$text."</listitem>\n";
5317 if ($block->{"last"}) {
5318 if ($block->{"ordered"}) {
5319 $tag = "orderedlist";
5321 $output .= "</".$tag.">\n";
5323 } elsif ($block->{"type"} eq "quote") {
5324 $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5325 $output .= "<blockquote>\n" . $text . "</blockquote>\n";
5326 } elsif ($block->{"type"} eq "code") {
5327 if ($block->{"language"}) {
5328 $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5330 $output .= "<informalexample><programlisting><![CDATA[\n";
5332 foreach (@{$block->{"lines"}}) {
5333 $output .= &ReplaceEntities ($_, $symbol) . "\n";
5335 $output .= "]]></programlisting></informalexample>\n";
5336 } elsif ($block->{"type"} eq "markup") {
5337 $text = &ExpandAbbreviations($symbol, $block->{"text"});
5338 $output .= $text."\n";
5340 $output .= $block->{"text"}."\n";
5347 sub MarkDownParseLines {
5348 my ($linesref, $symbol, $context) = @_;
5350 my @lines = @$linesref;
5353 @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5354 $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5360 my ($text, $symbol) = @_;
5363 # take out some variability in line endings
5364 $text =~ s%\r\n%\n%g;
5368 @lines = split("\n", $text);
5369 $text = MarkDownParseLines(\@lines, $symbol, "");
5374 #############################################################################
5375 # LIBRARY FUNCTIONS - These functions are used in both gtkdoc-mkdb and
5376 # gtkdoc-mktmpl and should eventually be moved to a
5378 #############################################################################
5380 #############################################################################
5381 # Function : ReadDeclarationsFile
5382 # Description : This reads in a file containing the function/macro/enum etc.
5385 # Note that in some cases there are several declarations with
5386 # the same name, e.g. for conditional macros. In this case we
5387 # set a flag in the %DeclarationConditional hash so the
5388 # declaration is not shown in the docs.
5390 # If a macro and a function have the same name, e.g. for
5391 # gtk_object_ref, the function declaration takes precedence.
5393 # Some opaque structs are just declared with 'typedef struct
5394 # _name name;' in which case the declaration may be empty.
5395 # The structure may have been found later in the header, so
5396 # that overrides the empty declaration.
5398 # Arguments : $file - the declarations file to read
5399 # $override - if declarations in this file should override
5400 # any current declaration.
5401 #############################################################################
5403 sub ReadDeclarationsFile {
5404 my ($file, $override) = @_;
5406 if ($override == 0) {
5408 %DeclarationTypes = ();
5409 %DeclarationConditional = ();
5410 %DeclarationOutput = ();
5414 || die "Can't open $file: $!";
5415 my $declaration_type = "";
5416 my $declaration_name;
5418 my $is_deprecated = 0;
5420 if (!$declaration_type) {
5421 if (m/^<([^>]+)>/) {
5422 $declaration_type = $1;
5423 $declaration_name = "";
5424 @TRACE@("Found declaration: $declaration_type\n");
5428 if (m%^<NAME>(.*)</NAME>%) {
5429 $declaration_name = $1;
5430 } elsif (m%^<DEPRECATED/>%) {
5432 } elsif (m%^</$declaration_type>%) {
5433 @TRACE@("Found end of declaration: $declaration_name\n");
5434 # Check that the declaration has a name
5435 if ($declaration_name eq "") {
5436 &LogWarning ($file, $., "$declaration_type has no name.\n");
5439 # If the declaration is an empty typedef struct _XXX XXX
5440 # set the flag to indicate the struct has a typedef.
5441 if ($declaration_type eq 'STRUCT'
5442 && $declaration =~ m/^\s*$/) {
5443 @TRACE@("Struct has typedef: $declaration_name\n");
5444 $StructHasTypedef{$declaration_name} = 1;
5447 # Check if the symbol is already defined.
5448 if (defined ($Declarations{$declaration_name})
5449 && $override == 0) {
5450 # Function declarations take precedence.
5451 if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5453 } elsif ($declaration_type eq 'FUNCTION') {
5454 if ($is_deprecated) {
5455 $Deprecated{$declaration_name} = "";
5457 $Declarations{$declaration_name} = $declaration;
5458 $DeclarationTypes{$declaration_name} = $declaration_type;
5459 } elsif ($DeclarationTypes{$declaration_name}
5460 eq $declaration_type) {
5461 # If the existing declaration is empty, or is just a
5462 # forward declaration of a struct, override it.
5463 if ($declaration_type eq 'STRUCT') {
5464 if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5465 if ($is_deprecated) {
5466 $Deprecated{$declaration_name} = "";
5468 $Declarations{$declaration_name} = $declaration;
5469 } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
5470 # Ignore an empty or forward declaration.
5472 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5475 # set flag in %DeclarationConditional hash for
5476 # multiply defined macros/typedefs.
5477 $DeclarationConditional{$declaration_name} = 1;
5480 &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5483 if ($is_deprecated) {
5484 $Deprecated{$declaration_name} = "";
5486 $Declarations{$declaration_name} = $declaration;
5487 $DeclarationTypes{$declaration_name} = $declaration_type;
5490 $declaration_type = "";
5501 #############################################################################
5502 # Function : ReadSignalsFile
5503 # Description : This reads in an existing file which contains information on
5504 # all GTK signals. It creates the arrays @SignalNames and
5505 # @SignalPrototypes containing info on the signals. The first
5506 # line of the SignalPrototype is the return type of the signal
5507 # handler. The remaining lines are the parameters passed to it.
5508 # The last parameter, "gpointer user_data" is always the same
5509 # so is not included.
5510 # Arguments : $file - the file containing the signal handler prototype
5512 #############################################################################
5514 sub ReadSignalsFile {
5522 my $signal_prototype;
5524 # Reset the signal info.
5525 @SignalObjects = ();
5527 @SignalReturns = ();
5529 @SignalPrototypes = ();
5534 if (!open (INPUT, $file)) {
5535 warn "Can't open $file - skipping signals\n";
5542 $signal_object = "";
5544 $signal_returns = "";
5545 $signal_prototype = "";
5548 if (m/^<NAME>(.*)<\/NAME>/) {
5550 if ($signal_name =~ m/^(.*)::(.*)$/) {
5551 $signal_object = $1;
5552 ($signal_name = $2) =~ s/_/-/g;
5553 @TRACE@("Found signal: $signal_name\n");
5555 &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5557 } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5558 $signal_returns = $1;
5559 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5561 } elsif (m%^</SIGNAL>%) {
5562 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5563 push (@SignalObjects, $signal_object);
5564 push (@SignalNames, $signal_name);
5565 push (@SignalReturns, $signal_returns);
5566 push (@SignalFlags, $signal_flags);
5567 push (@SignalPrototypes, $signal_prototype);
5570 $signal_prototype .= $_;
5578 #############################################################################
5579 # Function : ReadTemplateFile
5580 # Description : This reads in the manually-edited documentation file
5581 # corresponding to the file currently being created, so we can
5582 # insert the documentation at the appropriate places.
5583 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5584 # is a hash of arrays.
5585 # NOTE: This function is duplicated in gtkdoc-mktmpl (but
5586 # slightly different).
5587 # Arguments : $docsfile - the template file to read in.
5588 # $skip_unused_params - 1 if the unused parameters should be
5590 #############################################################################
5592 sub ReadTemplateFile {
5593 my ($docsfile, $skip_unused_params) = @_;
5595 my $template = "$docsfile.sgml";
5596 if (! -f $template) {
5597 @TRACE@("File doesn't exist: $template\n");
5601 # start with empty hashes, we merge the source comment for each file
5607 my $current_type = ""; # Type of symbol being read.
5608 my $current_symbol = ""; # Name of symbol being read.
5609 my $symbol_doc = ""; # Description of symbol being read.
5610 my @params; # Parameter names and descriptions of current
5611 # function/macro/function typedef.
5612 my $current_param = -1; # Index of parameter currently being read.
5613 # Note that the param array contains pairs
5614 # of param name & description.
5615 my $in_unused_params = 0; # True if we are reading in the unused params.
5616 my $in_deprecated = 0;
5618 my $in_stability = 0;
5620 open (DOCS, "$template")
5621 || die "Can't open $template: $!";
5623 @TRACE@("reading template $template");
5626 if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5629 if ($symbol eq "Title"
5630 || $symbol eq "Short_Description"
5631 || $symbol eq "Long_Description"
5632 || $symbol eq "See_Also"
5633 || $symbol eq "Stability_Level"
5634 || $symbol eq "Include"
5635 || $symbol eq "Image") {
5637 $symbol = $docsfile . ":" . $symbol;
5640 @TRACE@("Found symbol: $symbol\n");
5641 # Remember file and line for the symbol
5642 $SymbolSourceFile{$symbol} = $template;
5643 $SymbolSourceLine{$symbol} = $.;
5645 # Store previous symbol, but remove any trailing blank lines.
5646 if ($current_symbol ne "") {
5647 $symbol_doc =~ s/\s+$//;
5648 $SymbolTypes{$current_symbol} = $current_type;
5649 $SymbolDocs{$current_symbol} = $symbol_doc;
5651 # Check that the stability level is valid.
5652 if ($StabilityLevel{$current_symbol}) {
5653 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5656 if ($current_param >= 0) {
5657 $SymbolParams{$current_symbol} = [ @params ];
5659 # Delete any existing params in case we are overriding a
5660 # previously read template.
5661 delete $SymbolParams{$current_symbol};
5664 $current_type = $type;
5665 $current_symbol = $symbol;
5666 $current_param = -1;
5667 $in_unused_params = 0;
5674 } elsif (m/^<!-- # Unused Parameters # -->/) {
5675 @TRACE@("Found unused parameters\n");
5676 $in_unused_params = 1;
5679 } elsif ($in_unused_params && $skip_unused_params) {
5680 # When outputting the DocBook we skip unused parameters.
5681 @TRACE@("Skipping unused param: $_");
5685 # Check if param found. Need to handle "..." and "format...".
5686 if (s/^\@([\w\.]+):\040?//) {
5687 my $param_name = $1;
5688 my $param_desc = $_;
5689 # Allow variations of 'Returns'
5690 if ($param_name =~ m/^[Rr]eturns?$/) {
5691 $param_name = "Returns";
5693 # Allow varargs variations
5694 if ($param_name =~ m/^.*\.\.\.$/) {
5695 $param_name = "...";
5698 # strip trailing whitespaces and blank lines
5701 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5703 if ($param_name eq "Deprecated") {
5705 $Deprecated{$current_symbol} = $_;
5706 } elsif ($param_name eq "Since") {
5709 $Since{$current_symbol} = $_;
5710 } elsif ($param_name eq "Stability") {
5712 $StabilityLevel{$current_symbol} = $_;
5714 push (@params, $param_name);
5715 push (@params, $param_desc);
5716 $current_param += $PARAM_FIELD_COUNT;
5719 # strip trailing whitespaces and blank lines
5724 if ($in_deprecated) {
5725 $Deprecated{$current_symbol} .= $_;
5726 } elsif ($in_since) {
5727 &LogWarning ($template, $., "multi-line since docs found");
5728 #$Since{$current_symbol} .= $_;
5729 } elsif ($in_stability) {
5730 $StabilityLevel{$current_symbol} .= $_;
5731 } elsif ($current_param >= 0) {
5732 $params[$current_param] .= $_;
5741 # Remember to finish the current symbol doccs.
5742 if ($current_symbol ne "") {
5744 $symbol_doc =~ s/\s+$//;
5745 $SymbolTypes{$current_symbol} = $current_type;
5746 $SymbolDocs{$current_symbol} = $symbol_doc;
5748 # Check that the stability level is valid.
5749 if ($StabilityLevel{$current_symbol}) {
5750 $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5753 if ($current_param >= 0) {
5754 $SymbolParams{$current_symbol} = [ @params ];
5756 # Delete any existing params in case we are overriding a
5757 # previously read template.
5758 delete $SymbolParams{$current_symbol};
5767 #############################################################################
5768 # Function : ReadObjectHierarchy
5769 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5770 # the GtkObject subclasses described in this module (and their
5772 # It places them in the @Objects array, and places their level
5773 # in the object hierarchy in the @ObjectLevels array, at the
5774 # same index. GtkObject, the root object, has a level of 1.
5776 # This also generates tree_index.sgml as it goes along.
5779 #############################################################################
5781 sub ReadObjectHierarchy {
5785 if (! -f $OBJECT_TREE_FILE) {
5788 if (!open (INPUT, $OBJECT_TREE_FILE)) {
5789 warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5793 # Only emit objects if they are supposed to be documented, or if
5794 # they have documented children. To implement this, we maintain a
5795 # stack of pending objects which will be emitted if a documented
5797 my @pending_objects = ();
5798 my @pending_levels = ();
5804 my $level = (length($`)) / 2 + 1;
5811 while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5812 my $pobject = pop(@pending_objects);
5813 my $plevel = pop(@pending_levels);
5816 push (@pending_objects, $object);
5817 push (@pending_levels, $level);
5819 if (exists($KnownSymbols{$object})) {
5820 while ($#pending_levels >= 0) {
5821 $object = shift @pending_objects;
5822 $level = shift @pending_levels;
5823 $xref = &MakeXRef ($object);
5825 push (@tree, ' ' x ($level * 4) . "$xref");
5826 push (@Objects, $object);
5827 push (@ObjectLevels, $level);
5828 $ObjectRoots{$object} = $root;
5832 # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5838 # FIXME: use $OUTPUT_FORMAT
5839 # my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.$OUTPUT_FORMAT";
5840 my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml";
5841 my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new";
5843 open (OUTPUT, ">$new_tree_index")
5844 || die "Can't create $new_tree_index: $!";
5846 if ($OUTPUT_FORMAT eq "xml") {
5847 my $tree_header = $doctype_header;
5849 $tree_header =~ s/<!DOCTYPE \w+/<!DOCTYPE screen/;
5850 print (OUTPUT "$tree_header");
5852 print (OUTPUT "<screen>\n" . &AddTreeLineArt(\@tree) . "\n</screen>\n");
5855 &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5860 #############################################################################
5861 # Function : ReadInterfaces
5862 # Description : This reads in the $MODULE.interfaces file.
5865 #############################################################################
5867 sub ReadInterfaces {
5870 if (! -f $INTERFACES_FILE) {
5873 if (!open (INPUT, $INTERFACES_FILE)) {
5874 warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5880 my ($object, @ifaces) = split;
5881 if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5882 my @knownIfaces = ();
5884 # filter out private interfaces, but leave foreign interfaces
5885 foreach my $iface (@ifaces) {
5886 if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5887 push (@knownIfaces, $iface);
5891 $Interfaces{$object} = join(' ', @knownIfaces);
5897 #############################################################################
5898 # Function : ReadPrerequisites
5899 # Description : This reads in the $MODULE.prerequisites file.
5902 #############################################################################
5904 sub ReadPrerequisites {
5905 %Prerequisites = ();
5907 if (! -f $PREREQUISITES_FILE) {
5910 if (!open (INPUT, $PREREQUISITES_FILE)) {
5911 warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
5917 my ($iface, @prereqs) = split;
5918 if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
5919 my @knownPrereqs = ();
5921 # filter out private prerequisites, but leave foreign prerequisites
5922 foreach my $prereq (@prereqs) {
5923 if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
5924 push (@knownPrereqs, $prereq);
5928 $Prerequisites{$iface} = join(' ', @knownPrereqs);
5934 #############################################################################
5935 # Function : ReadArgsFile
5936 # Description : This reads in an existing file which contains information on
5937 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
5938 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
5940 # Arguments : $file - the file containing the arg information.
5941 #############################################################################
5956 # Reset the args info.
5969 if (!open (INPUT, $file)) {
5970 warn "Can't open $file - skipping args\n";
5987 if (m/^<NAME>(.*)<\/NAME>/) {
5989 if ($arg_name =~ m/^(.*)::(.*)$/) {
5991 ($arg_name = $2) =~ s/_/-/g;
5992 @TRACE@("Found arg: $arg_name\n");
5994 &LogWarning ($file, $., "Invalid argument name: $arg_name");
5996 } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
5998 } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6000 } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6002 } elsif (m/^<NICK>(.*)<\/NICK>/) {
6004 } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6006 if ($arg_blurb eq "(null)") {
6008 &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6010 } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6012 } elsif (m%^</ARG>%) {
6013 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6014 push (@ArgObjects, $arg_object);
6015 push (@ArgNames, $arg_name);
6016 push (@ArgTypes, $arg_type);
6017 push (@ArgRanges, $arg_range);
6018 push (@ArgFlags, $arg_flags);
6019 push (@ArgNicks, $arg_nick);
6020 push (@ArgBlurbs, $arg_blurb);
6021 push (@ArgDefaults, $arg_default);
6029 #############################################################################
6030 # Function : AddTreeLineArt
6031 # Description : Add unicode lineart to a pre-indented string array and returns
6032 # it as as multiline string.
6033 # Arguments : @tree - array of indented strings.
6034 #############################################################################
6036 sub AddTreeLineArt {
6037 my @tree = @{$_[0]};
6042 # iterate bottom up over the tree
6043 for ($i = $#tree; $i >= 0; $i--) {
6044 # count leading spaces
6045 $tree[$i] =~ /^([^<A-Za-z]*)/;
6046 $indent = length( $1 );
6047 # replace with ╰───, if place of ╰ is not space insert ├
6049 if (substr($tree[$i],$indent-4,1) eq " ") {
6050 substr($tree[$i],$indent-4,4) = "--- ";
6052 substr($tree[$i],$indent-4,4) = "+-- ";
6054 # go lines up while space and insert |
6055 for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6056 substr($tree[$j],$indent-4,1) = '|';
6061 my $res = join("\n", @tree);
6062 # unicode chars for: ╰──
6063 $res =~ s%---%<phrase role=\"lineart\">╰──</phrase>%g;
6064 # unicde chars for: ├──
6065 $res =~ s%\+--%<phrase role=\"lineart\">├──</phrase>%g;
6066 # unicode char for: │
6067 $res =~ s%\|%<phrase role=\"lineart\">│</phrase>%g;
6073 #############################################################################
6074 # Function : CheckIsObject
6075 # Description : Returns 1 if the given name is a GObject or a subclass.
6076 # It uses the global @Objects array.
6077 # Note that the @Objects array only contains classes in the
6078 # current module and their ancestors - not all GObject classes.
6079 # Arguments : $name - the name to check.
6080 #############################################################################
6084 my $root = $ObjectRoots{$name};
6085 # Let GBoxed pass as an object here to get -struct appended to the id
6086 # and prevent conflicts with sections.
6087 return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6091 #############################################################################
6092 # Function : MakeReturnField
6093 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6094 # Arguments : $str - the string to pad.
6095 #############################################################################
6097 sub MakeReturnField {
6100 return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6103 #############################################################################
6104 # Function : GetSymbolSourceFile
6105 # Description : Get the filename where the symbol docs where taken from.
6106 # Arguments : $symbol - the symbol name
6107 #############################################################################
6109 sub GetSymbolSourceFile {
6112 if (defined($SourceSymbolSourceFile{$symbol})) {
6113 return $SourceSymbolSourceFile{$symbol};
6114 } elsif (defined($SymbolSourceFile{$symbol})) {
6115 return $SymbolSourceFile{$symbol};
6121 #############################################################################
6122 # Function : GetSymbolSourceLine
6123 # Description : Get the file line where the symbol docs where taken from.
6124 # Arguments : $symbol - the symbol name
6125 #############################################################################
6127 sub GetSymbolSourceLine {
6130 if (defined($SourceSymbolSourceLine{$symbol})) {
6131 return $SourceSymbolSourceLine{$symbol};
6132 } elsif (defined($SymbolSourceLine{$symbol})) {
6133 return $SymbolSourceLine{$symbol};