mkdb: track incomplete docs in tmpl-free build too. Fixes 568711
[gtk-doc.git] / gtkdoc-mktmpl.in
blobf44986796d2132ec1ca701042ad060f8caa60e0a
1 #!@PERL@ -w
2 # -*- cperl -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998  Damon Chaplin
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #############################################################################
23 # Script      : gtkdoc-mktmpl
24 # Description : This creates or updates the template files which contain the
25 #               manually-edited documentation. (A 'template' is a simple text
26 #               form which is filled in with the description of a function,
27 #               macro, enum, or struct. For functions and macros it also
28 #               contains fields for describing the parameters.)
30 #               This script reads in the existing templates, found in
31 #               tmpl/*.sgml, moves these files to tmpl/*.sgml.bak, and then
32 #               recreates the .sgml files according to the structure given in
33 #               the file $MODULE-sections.txt.
35 #               Any new templates added, or new function parameters, are
36 #               marked with 'FIXME' so you can do a grep to see which parts
37 #               need updating.
39 #               Any templates which are no longer used (i.e. they are remove
40 #               from $MODULE-sections.txt) are placed in the file
41 #               tmpl/$MODULE-unused.sgml. If they are included again later
42 #               they are automatically copied back into position.
43 #               If you are certain that these templates will never be used
44 #               again you can delete them from tmpl/$MODULE-unused.sgml.
46 #               Any parameters to functions which are no longer used are
47 #               separated from the rest of the parameters with the line
48 #               '<!-- # Unused Parameters # -->'. It may be that the parameter
49 #               name has just been changed, in which case you can copy the
50 #               description to the parameter with the new name. You can delete
51 #               the unused parameter descriptions when no longer needed.
52 #############################################################################
54 use strict;
55 use Getopt::Long;
57 push @INC, '@PACKAGE_DATA_DIR@';
58 require "gtkdoc-common.pl";
60 # Options
62 # name of documentation module
63 my $MODULE;
64 my $TMPL_DIR;
65 my $FLAG_CHANGES;
66 my $PRINT_VERSION;
67 my $PRINT_HELP;
68 my $ONLY_SECTION_TMPL;
70 my %optctl = ('module' => \$MODULE,
71               'flag-changes' => \$FLAG_CHANGES,
72               'output-dir' => \$TMPL_DIR,
73               'only-section-tmpl' => \$ONLY_SECTION_TMPL,
74               'version' => \$PRINT_VERSION,
75               'help' => \$PRINT_HELP);
76 GetOptions(\%optctl, "module=s", "flag-changes!", "output-dir:s", "only-section-tmpl!", "version", "help");
78 if ($PRINT_VERSION) {
79     print "@VERSION@\n";
80     exit 0;
83 if (!$MODULE) {
84     $PRINT_HELP = 1;
87 if ($PRINT_HELP) {
88     print <<EOF;
89 gtkdoc-mktmpl version @VERSION@ - generate documentation templates
91 --module=MODULE_NAME Name of the doc module being parsed
92 --flag-changes       If specified, changes in templates are flagged
93 --output-dir=DIRNAME The directory where the results are stored
94 --only-section-tmpl  Only include section information in templates
95 --version            Print the version of this program
96 --help               Print this help
97 EOF
98     exit 0;
101 my $ROOT_DIR = ".";
103 # The directory containing the template files.
104 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
106 # This file contains the object hierarchy.
107 my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
109 # The file containing signal handler prototype information.
110 my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
112 # The file containing Arg information.
113 my $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
115 # Set the flag to indicate changes, if requested.
116 my $CHANGES_FLAG = $FLAG_CHANGES ? "FIXME" : "";
118 # These global arrays store information on signals. Each signal has an entry
119 # in each of these arrays at the same index, like a multi-dimensional array.
120 my @SignalObjects;      # The GtkObject which emits the signal.
121 my @SignalNames;        # The signal name.
122 my @SignalReturns;      # The return type.
123 my @SignalFlags;        # Flags for the signal
124 my @SignalPrototypes;   # The rest of the prototype of the signal handler.
126 # These global arrays store information on Args. Each Arg has an entry
127 # in each of these arrays at the same index, like a multi-dimensional array.
128 my @ArgObjects;         # The GtkObject which has the Arg.
129 my @ArgNames;           # The Arg name.
130 my @ArgTypes;           # The Arg type - gint, GtkArrowType etc.
131 my @ArgFlags;           # How the Arg can be used - readable/writable etc.
133 # These global hashes store declaration info keyed on a symbol name.
134 my %Declarations;
135 my %DeclarationTypes;
136 my %DeclarationConditional;
137 my %DeclarationOutput;
139 # These global hashes store the existing documentation.
140 my %SymbolDocs;
141 my %SymbolTypes;
142 my %SymbolParams;
143 my %SymbolSourceFile;
144 my %SymbolSourceLine;
146 # These global arrays store GObject and subclasses and the hierarchy.
147 my @Objects;
148 my @ObjectLevels;
150 &ReadSignalsFile ($SIGNALS_FILE);
151 &ReadArgsFile ($ARGS_FILE);
152 &ReadObjectHierarchy;
154 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
155 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
156     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
158 &ReadExistingTemplates;
160 my $changed = 0;
162 if (&UpdateTemplates ("$ROOT_DIR/$MODULE-sections.txt")) {
163   $changed = 1;
165 &OutputUnusedTemplates;
166 if (&CheckAllDeclarationsOutput) {
167   $changed = 1;
170 if ($changed || ! -e "$ROOT_DIR/tmpl.stamp") {
171     open (TIMESTAMP, ">$ROOT_DIR/tmpl.stamp")
172         || die "Can't create $ROOT_DIR/tmpl.stamp";
173     print (TIMESTAMP "timestamp");
174     close (TIMESTAMP);
177 #############################################################################
178 # Function    : ReadExistingTemplates
179 # Description : This reads in all the existing documentation, into the global
180 #               variables %SymbolDocs, %SymbolTypes, and %SymbolParams (a
181 #               hash of arrays).
182 # Arguments   : none
183 #############################################################################
185 sub ReadExistingTemplates {
186     %SymbolDocs = ();
187     %SymbolTypes = ();
188     %SymbolParams = ();
190     # Read the unused docs first, so they get overridden by any real docs.
191     # (though this shouldn't happen often).
192     my $unused_doc = "$TMPL_DIR/$MODULE-unused.sgml";
193     if (-e $unused_doc) {
194         &ReadTemplateFile ($unused_doc, 0);
195     }
197     while (<$TMPL_DIR/*.sgml>) {
198 #       print "Reading $_\n";
199         if ($_ eq $unused_doc) {
200 #           print "skipping $unused_doc\n";
201         } else {
202             &ReadTemplateFile ($_, 0);
203         }
204     }
208 #############################################################################
209 # Function    : UpdateTemplates
210 # Description : This collects the output for each section of the docs, and
211 #               outputs each file when the end of the section is found.
212 # Arguments   : $file - the file containing the sections of the docs.
213 #############################################################################
215 sub UpdateTemplates {
216     my ($file) = @_;
217 #    print "Reading: $file\n";
219     open (INPUT, $file)
220         || die "Can't open $file";
222     # Create the top output directory if it doesn't exist.
223     if (! -e $TMPL_DIR) {
224         mkdir ("$TMPL_DIR", 0777)
225             || die "Can't create directory: $TMPL_DIR";
226     }
228     my $filename = "";
229     my $title = "";
230     my $subsection = "";
231     my $output;
232     my $changed = 0;
233     while (<INPUT>) {
234         if (m/^#/) {
235             next;
237         } elsif (m/^<SECTION>/) {
238             $output = "";
240         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
241             $subsection = $1;
242             next;
244         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
245             $title = $1;
246 #           print "Section: $title\n";
248         } elsif (m/^<FILE>(.*)<\/FILE>/) {
249             $filename = $1;
251         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
252             next;
254         } elsif (m/^<\/SECTION>/) {
255             if ($title eq "") {
256                 $title = $filename;
257             }
258 #           print "End of section: $title\n";
260             $filename =~ s/\s/_/g;
261             $filename .= ".sgml";
263             if (&OutputTemplateFile ($filename, $title, \$output)) {
264               $changed = 1;
265             }
267             $title = "";
268             $subsection = "";
270         } elsif (m/^(\S+)/) {
271             my $symbol = $1;
272 #           print "  Symbol: $symbol\n";
274             my $declaration = $Declarations{$1};
275             if (defined ($declaration)) {
276                 # We don't want templates for standard macros/functions of
277                 # GObjects or private declarations.
278                 if ($subsection ne "Standard" && $subsection ne "Private") {
279                     $output .= &OutputDeclaration ($DeclarationTypes {$symbol},
280                                                    $symbol, $declaration);
282                     $output .= &OutputSignalTemplates ($symbol);
283                     $output .= &OutputArgTemplates ($symbol);
284                 }
286                 # Note that the declaration has been output.
287                 $DeclarationOutput{$symbol} = 1;
289                 if ($declaration eq '##conditional##') {
290 #                   print "Conditional $DeclarationTypes{$symbol}\n";
291                 }
293             } else {
294                 &LogWarning ($file, $., "No declaration found for: $1");
295             }
296         }
297     }
298     close (INPUT);
300     return $changed;
304 #############################################################################
305 # Function    : CheckAllDeclarationsOutput
306 # Description : This steps through all the declarations that were loaded, and
307 #               makes sure that each one has been output, by checking the
308 #               corresponding flag in the %DeclarationOutput hash. It is
309 #               intended to check that any new declarations in new versions
310 #               of the module get added to the $MODULE-sections.txt file.
311 # Arguments   : none
312 #############################################################################
314 sub CheckAllDeclarationsOutput {
315     my $num_unused = 0;
317     my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
318     my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
320     open (UNUSED, ">$new_unused_file")
321         || die "Can't open $new_unused_file";
322     my ($symbol);
323     foreach $symbol (sort keys (%Declarations)) {
324         if (!defined ($DeclarationOutput{$symbol})) {
325             print (UNUSED "$symbol\n");
326             $num_unused++;
327         }
328     }
329     close (UNUSED);
330     if ($num_unused != 0) {
331         &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
332             "They should be added to $MODULE-sections.txt in the appropriate place.");
333     }
335     return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
339 #############################################################################
340 # Function    : OutputDeclaration
341 # Description : This returns the template for one symbol & declaration.
342 #               Note that it uses the global %SymbolDocs and %SymbolParams to
343 #               lookup any existing documentation.
344 # Arguments   : $type - the type of the symbol ('FUNCTION'/'MACRO' etc.)
345 #               $symbol - the symbol name.
346 #               $declaration - the declaration of the symbol.
347 #############################################################################
349 sub OutputDeclaration {
350     my ($type, $symbol, $declaration) = @_;
351     my ($output) = "";
353     #print "Outputting $type: $symbol\n";
355     # See if symbol already has a description.
356     my ($symbol_desc) = $SymbolDocs{$symbol};
357     my ($template_exists);
358     if (defined ($symbol_desc)) {
359         $template_exists = 1;
360         $symbol_desc =~ s/\s+$//;
361     } else {
362         $template_exists = 0;
363         $symbol_desc = "<para>\n$CHANGES_FLAG\n</para>";
364     }
366     $output .= <<EOF;
367 <!-- ##### $type $symbol ##### -->
368 $symbol_desc
372     # For functions, function typedefs and macros, we output the arguments.
373     # For functions and function typedefs we also output the return value.
374     if ($type eq "FUNCTION" || $type eq "USER_FUNCTION") {
375         # Take out the return type
376         $declaration =~ s/<RETURNS>\s*((const\s+|G_CONST_RETURN\s+|unsigned\s+|struct\s+|enum\s+)*)(\w+)\s*(\**\s*(const|G_CONST_RETURN)?\s*\**\s*(restrict)?\s*)<\/RETURNS>\n//;
377         my $ret_type_modifier = defined($1) ? $1 : "";
378         my $ret_type = $3;
379         my $ret_type_pointer = $4;
381         my ($param_num) = 0;
382         while ($declaration ne "") {
383             if ($declaration =~ s/^[\s,]+//) {
384                 # skip whitespace and commas
385                 next;
387             } elsif ($declaration =~ s/^void\s*[,\n]//) {
388                 if ($param_num != 0) {
389                     &LogWarning ($SymbolSourceFile{$symbol},$SymbolSourceLine{$symbol}, "void used as parameter in function $symbol");
390                 }
392             } elsif ($declaration =~ s/^...\s*[,\n]//) {
393                 $output .= &OutputParam ($symbol, "Varargs",
394                                          $template_exists, 1, "");
396             # Try to match a standard parameter (keep in sync with gtkdoc-mkdb)
397             #                                $1                                                                                                                                    $2                             $3                                                           $4       $5
398             } elsif ($declaration =~ s/^\s*((?:G_CONST_RETURN|G_GNUC_UNUSED|unsigned long|unsigned short|signed long|signed short|unsigned|signed|long|short|volatile|const)\s+)*((?:struct\b|enum\b)?\s*\w+)\s*((?:(?:const\b|restrict\b)?\s*\*?\s*(?:const\b|restrict\b)?\s*)*)(\w+)?\s*((?:\[\S*\])*)\s*[,\n]//) {
399                 my $pre         = defined($1) ? $1 : "";
400                 my $vtype       = $2;
401                 my $ptr         = defined($3) ? $3 : "";
402                 my $name        = defined($4) ? $4 : "";
404                 $pre   =~ s/\s+/ /g;
405                 $vtype =~ s/\s+/ /g;
406                 $ptr   =~ s/\s+/ /g;
407                 $ptr   =~ s/\s+$//;
408                 if ($ptr && $ptr !~ m/\*$/) { $ptr .= " "; }
410                 if (($name eq "") && $pre =~ m/^((un)?signed .*)\s?/ ) {
411                     $name  = $vtype;
412                     $vtype = "$1";
413                     $pre   = "";
414                 }
416                 #print "$symbol: '$pre' '$vtype' '$ptr' '$name' \n";
418                 if ($name eq "") {
419                     $name = "Param" . ($param_num + 1);
420                 }
421                 $output .= &OutputParam ($symbol, $name, $template_exists, 1, "");
423             # Try to match parameters which are functions (keep in sync with gtkdoc-mkdb)
424             #                              $1                                       $2          $3      $4                      $5                    $7             $8
425             } elsif ($declaration =~ s/^(const\s+|G_CONST_RETURN\s+|unsigned\s+)*(struct\s+)?(\w+)\s*(\**)\s*(?:restrict\b)?\s*(const\s+)?\(\s*\*+\s*(\w+)\s*\)\s*\(([^)]*)\)\s*[,\n]//) {
426                 my $name = $6;
427                 $output .= &OutputParam ($symbol, $name, $template_exists, 1,
428                                          "");
430             } else {
431                 &LogWarning ($SymbolSourceFile{$symbol},$SymbolSourceLine{$symbol}, 
432                     "Can't parse args for function $symbol: $declaration");
433                 last;
434             }
435             $param_num++;
436         }
438         if ($ret_type ne "void" || $ret_type_modifier || $ret_type_pointer) {
439             $output .= &OutputParam ($symbol, "Returns", $template_exists, 1,
440                                      "");
441         }
442         $output .= &OutputParam ($symbol, "Deprecated", $template_exists, 0, "");
443         $output .= &OutputParam ($symbol, "Since", $template_exists, 0, "");
444         $output .= &OutputParam ($symbol, "Stability", $template_exists, 0, "");
445         $output .= &OutputOldParams ($symbol);
446         $output .= "\n";
447     }
449     if ($type eq "MACRO") {
450         if ($declaration =~ m/^\s*#\s*define\s+\w+\(([^\)]*)\)/) {
451             my ($params) = $1;
452             my ($param);
454             $params =~ s/\\\n//g;
455             foreach $param (split (/,/, $params)) {
456                 $param =~ s/^\s+//;
457                 $param =~ s/\s*$//;
458                 if ($param =~ m/\S/) {
459                     $output .= &OutputParam ($symbol, $param, $template_exists,
460                                              1, "");
461                 }
462             }
463         }
464         $output .= &OutputParam ($symbol, "Returns", $template_exists, 0, "");
465         $output .= &OutputParam ($symbol, "Deprecated", $template_exists, 0, "");
466         $output .= &OutputParam ($symbol, "Since", $template_exists, 0, "");
467         $output .= &OutputParam ($symbol, "Stability", $template_exists, 0, "");
468         $output .= &OutputOldParams ($symbol);
469         $output .= "\n";
470     }
472     if ($type eq "STRUCT") {
473         my $is_object_struct = CheckIsObject ($symbol);
474         my @fields = ParseStructDeclaration($declaration, $is_object_struct, 1);
476         for (my $i = 0; $i <= $#fields; $i += 2) {
477             my $field_name = $fields[$i];
478             $output .= &OutputParam ($symbol, $field_name, $template_exists, 1, "");
479         }
480         $output .= &OutputParam ($symbol, "Deprecated", $template_exists, 0, "");
481         $output .= &OutputParam ($symbol, "Since", $template_exists, 0, "");
482         $output .= &OutputParam ($symbol, "Stability", $template_exists, 0, "");
483     }
485     if ($type eq "ENUM") {
486         my @members = ParseEnumDeclaration($declaration);
488         for my $member (@members) {
489             $output .= &OutputParam ($symbol, $member, $template_exists, 1, "");
490         }
491         $output .= &OutputParam ($symbol, "Deprecated", $template_exists, 0, "");
492         $output .= &OutputParam ($symbol, "Since", $template_exists, 0, "");
493         $output .= &OutputParam ($symbol, "Stability", $template_exists, 0, "");
494     }
496     $output .= "\n";
498     # Remove the used docs from the hashes.
499     if ($template_exists) {
500         delete $SymbolDocs{$symbol};
501         delete $SymbolParams{$symbol};
502     }
504     return $output;
508 #############################################################################
509 # Function    : OutputParam
510 # Description : This outputs the part of a template for one parameter.
511 #               It first checks if the parameter is already described, and if
512 #               so it uses that description, and clears it so it isn't output
513 #               as an old param.
514 # Arguments   : $symbol - the symbol (function or macro) name.
515 #               $param_to_output - the parameter to add.
516 #               $template_exists - TRUE if the template already existed in
517 #                 template files. If it did, then we will flag any changes
518 #                 with 'FIXME'.
519 #               $force_output - TRUE if the parameter should be output even
520 #                 if it didn't already exist in the template. (The return
521 #                 values of macros are added manually if required, and so we
522 #                 never add it here - we only copy it if it already exists.)
523 #               $default_description - the default description of the
524 #                 parameter to be used if it doesn't already exist. (Signal
525 #                 handlers have a few common parameters.)
526 #############################################################################
528 sub OutputParam {
529     my ($symbol, $param_to_output, $template_exists,
530         $force_output, $default_description) = @_;
531     my ($j);
533     my ($params) = $SymbolParams{$symbol};
534     if (defined ($params)) {
535         for ($j = 0; $j <= $#$params; $j += 2) {
536             my $param_name = $$params[$j];
537             my $param_desc = $$params[$j + 1];
539             if ($param_name eq $param_to_output) {
540                 $param_desc =~ s/\s+$//;
541                 $$params[$j] = "";
542                 $$params[$j + 1] = "";
543                 return "\@$param_name: $param_desc\n";
544             }
545         }
546     }
548     # If the template was already in a file, flag the new parameter.
549     # If not, the template itself will be flagged, so we don't need to flag
550     # all the new parameters as well.
551     if ($force_output) {
552         if ($default_description ne "") {
553             $default_description =~ s/\s+$//;
554             return "\@$param_to_output: $default_description\n";
555         } else {
556             if ($template_exists) {
557                 return "\@$param_to_output: $CHANGES_FLAG\n";
558             } else {
559                 return "\@$param_to_output: \n";
560             }
561         }
562     }
563     return "";
567 #############################################################################
568 # Function    : OutputOldParams
569 # Description : This returns all the existing documentation for parameters of
570 #               the given function/macro/signal symbol which are unused, with
571 #               a comment before them.
572 # Arguments   : $symbol - the symbol (function/macro/signal) name.
573 #############################################################################
575 sub OutputOldParams {
576     my ($symbol) = @_;
577     my $output = "";
579     my ($params) = $SymbolParams{$symbol};
580     if (defined ($params)) {
581         my $j;
582         for ($j = 0; $j <= $#$params; $j += 2) {
583             my $param_name = $$params[$j];
584             my $param_desc = $$params[$j + 1];
586             if ($param_name ne "") {
587                 $param_desc =~ s/\s+$//;
589                 # There's no point keeping it if it has no docs.
590                 if ($param_desc ne "") {
591                   $output .= "\@$param_name: $param_desc\n";
592                 }
593             }
594         }
595     }
596     if ($output) {
597         $output = "<!-- # Unused Parameters # -->\n" . $output;
598     }
599     return $output;
603 #############################################################################
604 # Function    : OutputTemplateFile
605 # Description : This outputs one template file.
606 # Arguments   : $file - the basename of the file to output.
607 #               $title - the title from the $MODULE-sections.txt file. This
608 #                 will be overridden by any title given in the template file.
609 #               $output - reference to the templates to output.
610 #############################################################################
612 sub OutputTemplateFile {
613     my ($file, $title, $output) = @_;
615     my ($short_desc, $long_desc, $see_also, $stability);
617     if (defined ($SymbolDocs{"$TMPL_DIR/$file:Title"})) {
618         $title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
619         delete $SymbolDocs{"$TMPL_DIR/$file:Title"};
620     }
621     if (defined ($SymbolDocs{"$TMPL_DIR/$file:Short_Description"})) {
622         $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
623         delete $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
624     } else {
625         $short_desc = "";
626     }
627     if (defined ($SymbolDocs{"$TMPL_DIR/$file:Long_Description"})) {
628         $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
629         delete $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
630     } else {
631         $long_desc = "<para>\n\n</para>\n";
632     }
633     if (defined ($SymbolDocs{"$TMPL_DIR/$file:See_Also"})) {
634         $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
635         delete $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
636     } else {
637         $see_also = "<para>\n\n</para>\n";
638     }
639     if (defined ($SymbolDocs{"$TMPL_DIR/$file:Stability_Level"})) {
640         $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
641         delete $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
642     } else {
643         $stability = "";
644     }
647     my $old_tmpl_file = "$TMPL_DIR/$file";
648     my $new_tmpl_file = "$TMPL_DIR/$file.new";
650     open (OUTPUT, ">$new_tmpl_file")
651         || die "Can't create $new_tmpl_file";
653     print (OUTPUT <<EOF);
654 <!-- ##### SECTION Title ##### -->
655 $title
657 <!-- ##### SECTION Short_Description ##### -->
658 $short_desc
660 <!-- ##### SECTION Long_Description ##### -->
661 $long_desc
663 <!-- ##### SECTION See_Also ##### -->
664 $see_also
666 <!-- ##### SECTION Stability_Level ##### -->
667 $stability
671     print (OUTPUT $$output) unless $ONLY_SECTION_TMPL;
672     close (OUTPUT);
674     return &UpdateFileIfChanged ($old_tmpl_file, $new_tmpl_file, 1);
678 #############################################################################
679 # Function    : OutputSignalTemplates
680 # Description : Outputs templates for signal handlers.
681 # Arguments   : $title - the title from the $MODULE-sections.txt file. If the
682 #                 file is describing a GtkObject subclass, the title should
683 #                 be the name of the class, e.g. 'GtkButton'.
684 #############################################################################
686 sub OutputSignalTemplates {
687     my ($title) = @_;
689     my $output = "";
690     my ($i, $template_exists);
691     for ($i = 0; $i <= $#SignalObjects; $i++) {
692         if ($SignalObjects[$i] eq $title) {
693 #           print "Found signal: $SignalObjects[$i]\n";
694             my ($symbol) = "$SignalObjects[$i]::$SignalNames[$i]";
696             # See if symbol already has a description.
697             my ($symbol_desc) = $SymbolDocs{$symbol};
698             if (defined ($symbol_desc)) {
699                 $template_exists = 1;
700                 $symbol_desc =~ s/\s+$//;
701                 delete $SymbolDocs{$symbol};
702             } else {
703                 $template_exists = 0;
704                 $symbol_desc = "<para>\n$CHANGES_FLAG\n</para>";
705             }
707             $output .= <<EOF;
708 <!-- ##### SIGNAL $symbol ##### -->
709 $symbol_desc
712             my $sourceparams = $SymbolParams{$symbol};
713             my @params = split ("[,\n]", $SignalPrototypes[$i]);
714             my ($j, $name);
715             for ($j = 0; $j <= $#params; $j++) {
716                 my $param = $params[$j];
717                 $param =~ s/^\s+//;
718                 $param =~ s/\s*$//;
719                 if ($param =~ m/^\s*$/) { next; }
720                 if ($param =~ m/^void$/) { next; }
722                 if ($param =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)?\s*$/) {
723                     if (defined ($sourceparams)) {
724                         $name = $$sourceparams[2 * $j];
725                     } else {
726                         $name = $3;
727                     }
729                     if (!defined ($name)) {
730                         $name = "Param" . ($j + 1);
731                     }
733                     if ($j == 0) {
734                         $output .= &OutputParam ($symbol, $name,
735                                                  $template_exists, 1,
736                                                  "the object which received the signal.");
737                     } else {
738                         $output .= &OutputParam ($symbol, $name,
739                                                  $template_exists, 1, "");
740                     }
741                 }
742             }
744             if ($SignalReturns[$i] ne "void") {
745                 $output .= &OutputParam ($symbol, "Returns", $template_exists,
746                                          1, "");
747             }
748             $output .= &OutputOldParams ($symbol);
749             $output .= "\n";
750         }
751     }
752     return $output;
756 #############################################################################
757 # Function    : OutputArgTemplates
758 # Description : Outputs templates for Args.
759 # Arguments   : $title - the title from the $MODULE-sections.txt file. If the
760 #                 file is describing a GtkObject subclass, the title should
761 #                 be the name of the class, e.g. 'GtkButton'.
762 #############################################################################
764 sub OutputArgTemplates {
765     my ($title) = @_;
767     my $output = "";
768     my $i;
769     for ($i = 0; $i <= $#ArgObjects; $i++) {
770         if ($ArgObjects[$i] eq $title) {
771 #           print "Found arg: $ArgObjects[$i]\n";
772             # I've only used one colon so we don't clash with signals.
773             my ($symbol) = "$ArgObjects[$i]:$ArgNames[$i]";
775             # See if symbol already has a description.
776             my ($symbol_desc) = $SymbolDocs{$symbol};
777             if (defined ($symbol_desc)) {
778                 delete $SymbolDocs{$symbol};
779                 $symbol_desc =~ s/\s+$//;
780             } else {
781                 $symbol_desc = "<para>\n$CHANGES_FLAG\n</para>";
782             }
784             $output .= <<EOF;
785 <!-- ##### ARG $symbol ##### -->
786 $symbol_desc
789         }
790     }
791     return $output;
795 #############################################################################
796 # Function    : OutputUnusedTemplates
797 # Description : This saves any unused documentation into $MODULE-unused.sgml.
798 # Arguments   : none
799 #############################################################################
801 sub OutputUnusedTemplates {
802     my ($old_unused_file) = "$TMPL_DIR/$MODULE-unused.sgml";
803     my ($new_unused_file) = "$TMPL_DIR/$MODULE-unused.new";
805     open (UNUSED, ">$new_unused_file")
806         || die "Can't open file: $new_unused_file";
808     my $output = "";
809     my ($symbol, $symbol_desc);
810     for $symbol (sort keys %SymbolDocs) {
811         $symbol_desc = $SymbolDocs{$symbol};
813 #       print "Unused: $symbol\n";
815         my $type = $SymbolTypes{$symbol};
816         if (!defined ($type)) {
817             $type = "UNKNOWN";
818             &LogWarning ($SymbolSourceFile{$symbol},$SymbolSourceLine{$symbol}, "Unused symbol $symbol has unknown type.");
819         }
821     $output .= <<EOF;
822 <!-- ##### $type $symbol ##### -->
823 $symbol_desc
827         my ($params) = $SymbolParams{$symbol};
828         if (defined ($params)) {
829             my $j;
830             for ($j = 0; $j <= $#$params; $j += 2) {
831                 my $param_name = $$params[$j];
832                 my $param_desc = $$params[$j + 1];
833                 $param_desc =~ s/\s+$//;
834                 $output .= "\@$param_name: $param_desc\n";
835             }
836         }
837         $output .= "\n";
838     }
840     print UNUSED $output;
841     close (UNUSED);
843     &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 1);
847 #############################################################################
848 # LIBRARY FUNCTIONS -   These functions are used in both gtkdoc-mkdb and
849 #                       gtkdoc-mktmpl and should eventually be moved to a
850 #                       separate library.
851 #############################################################################
853 #############################################################################
854 # Function    : ReadDeclarationsFile
855 # Description : This reads in a file containing the function/macro/enum etc.
856 #               declarations.
858 #               Note that in some cases there are several declarations with
859 #               the same name, e.g. for conditional macros. In this case we
860 #               set a flag in the %DeclarationConditional hash so the
861 #               declaration is not shown in the docs.
863 #               If a macro and a function have the same name, e.g. for
864 #               gtk_object_ref, the function declaration takes precedence.
866 #               Some opaque structs are just declared with 'typedef struct
867 #               _name name;' in which case the declaration may be empty.
868 #               The structure may have been found later in the header, so
869 #               that overrides the empty declaration.
871 # Arguments   : $file - the declarations file to read
872 #               $override - if declarations in this file should override
873 #                       any current declaration.
874 #############################################################################
876 sub ReadDeclarationsFile {
877     my ($file, $override) = @_;
879     if ($override == 0) {
880         %Declarations = ();
881         %DeclarationTypes = ();
882         %DeclarationConditional = ();
883         %DeclarationOutput = ();
884     }
886     open (INPUT, $file)
887         || die "Can't open $file";
888     my $declaration_type = "";
889     my $declaration_name;
890     my $declaration;
891     while (<INPUT>) {
892         if (!$declaration_type) {
893             if (m/^<([^>]+)>/) {
894                 $declaration_type = $1;
895                 $declaration_name = "";
896 #               print "Found declaration: $declaration_type\n";
897                 $declaration = "";
898             }
899         } else {
900             if (m%^<NAME>(.*)</NAME>%) {
901                 $declaration_name = $1;
902             } elsif (m%<DEPRECATED/>%) {
903                 # do nothing, just skip the line; we handle this
904                 # in mkdb
905             } elsif (m%^</$declaration_type>%) {
906 #               print "Found end of declaration: $declaration_name\n";
907                 # Check that the declaration has a name
908                 if ($declaration_name eq "") {
909                     print "ERROR: $declaration_type has no name $file:$.\n";
910                 }
912                 # Check if the symbol is already defined.
913                 if (defined ($Declarations{$declaration_name})
914                     && $override == 0) {
915                     # Function declarations take precedence.
916                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
917                         # Ignore it.
918                     } elsif ($declaration_type eq 'FUNCTION') {
919                         $Declarations{$declaration_name} = $declaration;
920                         $DeclarationTypes{$declaration_name} = $declaration_type;
921                     } elsif ($DeclarationTypes{$declaration_name}
922                               eq $declaration_type) {
923                         # If the existing declaration is empty, or is just a
924                         # forward declaration of a struct, override it.
925                         if ($declaration_type eq 'STRUCT') {
926                             if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
927                                 $Declarations{$declaration_name} = $declaration;
928                             } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
929                                 # Ignore an empty or forward declaration.
930                             } else {
931                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
932                             }
934                         } else {
935                             # set flag in %DeclarationConditional hash for
936                             # multiply defined macros/typedefs.
937                             $DeclarationConditional{$declaration_name} = 1;
938                         }
939                     } else {
940                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
941                     }
942                 } else {
943                     $Declarations{$declaration_name} = $declaration;
944                     $DeclarationTypes{$declaration_name} = $declaration_type;
945                 }
946                 $declaration_type = "";
947             } else {
948                 $declaration .= $_;
949             }
950         }
951     }
952     close (INPUT);
956 #############################################################################
957 # Function    : ReadSignalsFile
958 # Description : This reads in an existing file which contains information on
959 #               all GObject signals. It creates the arrays @SignalNames and
960 #               @SignalPrototypes containing info on the signals. The first
961 #               line of the SignalPrototype is the return type of the signal
962 #               handler. The remaining lines are the parameters passed to it.
963 #               The last parameter, "gpointer user_data" is always the same
964 #               so is not included.
965 # Arguments   : $file - the file containing the signal handler prototype
966 #                       information.
967 #############################################################################
969 sub ReadSignalsFile {
970     my ($file) = @_;
972     my $in_signal = 0;
973     my $signal_object;
974     my $signal_name;
975     my $signal_returns;
976     my $signal_flags;
977     my $signal_prototype;
979     # Reset the signal info.
980     @SignalObjects = ();
981     @SignalNames = ();
982     @SignalReturns = ();
983     @SignalFlags = ();
984     @SignalPrototypes = ();
986     if (! -f $file) {
987         return;
988     }
989     if (!open (INPUT, $file)) {
990         warn "Can't open $file - skipping signals\n";
991         return;
992     }
993     while (<INPUT>) {
994         if (!$in_signal) {
995             if (m/^<SIGNAL>/) {
996                 $in_signal = 1;
997                 $signal_object = "";
998                 $signal_name = "";
999                 $signal_returns = "";
1000                 $signal_prototype = "";
1001             }
1002         } else {
1003             if (m/^<NAME>(.*)<\/NAME>/) {
1004                 $signal_name = $1;
1005                 if ($signal_name =~ m/^(.*)::(.*)$/) {
1006                     $signal_object = $1;
1007                     ($signal_name = $2) =~ s/_/-/g;
1008 #                   print "Found signal: $signal_name\n";
1009                 } else {
1010                     print "Invalid signal name: $signal_name\n";
1011                 }
1012             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
1013                 $signal_returns = $1;
1014             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
1015                 $signal_flags = $1;
1016             } elsif (m%^</SIGNAL>%) {
1017 #               print "Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}";
1018                 push (@SignalObjects, $signal_object);
1019                 push (@SignalNames, $signal_name);
1020                 push (@SignalReturns, $signal_returns);
1021                 push (@SignalFlags, $signal_flags);
1022                 push (@SignalPrototypes, $signal_prototype);
1023                 $in_signal = 0;
1024             } else {
1025                 $signal_prototype .= $_;
1026             }
1027         }
1028     }
1029     close (INPUT);
1033 #############################################################################
1034 # Function    : ReadTemplateFile
1035 # Description : This reads in the manually-edited documentation file
1036 #               corresponding to the file currently being created, so we can
1037 #               insert the documentation at the appropriate places.
1038 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
1039 #               is a hash of arrays.
1040 #               NOTE: This function is duplicated in gtkdoc-mkdb (but
1041 #               slightly different).
1042 # Arguments   : $docsfile - the template file to read in.
1043 #               $skip_unused_params - 1 if the unused parameters should be
1044 #                       skipped.
1045 #############################################################################
1047 sub ReadTemplateFile {
1048     my ($docsfile, $skip_unused_params) = @_;
1050 #    print "Reading $docsfile\n";
1051     if (! -f $docsfile) {
1052         print "File doesn't exist: $docsfile\n";
1053         return 0;
1054     }
1056     my $CurrentType = "";       # Type of symbol being read.
1057     my $CurrentSymbol = "";     # Name of symbol being read.
1058     my $SymbolDoc = "";         # Description of symbol being read.
1059     my @Params;                 # Parameter names and descriptions of current
1060                                 #   function/macro/function typedef.
1061     my $CurrentParam = -1;      # Index of parameter currently being read.
1062                                 #   Note that the param array contains pairs
1063                                 #   of param name & description.
1064     my $InUnusedParameters = 0; # True if we are reading in the unused params.
1066     open (DOCS, $docsfile)
1067         || die "Can't open file $docsfile: $!";
1068     while (<DOCS>) {
1069         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
1070             my $type = $1;
1071             my $symbol = $2;
1072             if ($symbol eq "Title"
1073                 || $symbol eq "Short_Description"
1074                 || $symbol eq "Long_Description"
1075                 || $symbol eq "See_Also"
1076                 || $symbol eq "Stability_Level") {
1077                 $symbol = $docsfile . ":" . $symbol;
1078             }
1080             #print "Found symbol: $symbol\n";
1081             # Remember file and line for the symbol
1082             $SymbolSourceFile{$symbol} = $docsfile;
1083             $SymbolSourceLine{$symbol} = $.;
1085             # Canonicalize signal and argument names to have -, not _
1086             if ($type eq "ARG" || $type eq "SIGNAL") {
1087               $symbol =~ s/_/-/g;
1088             }
1090             # Store previous symbol, but remove any trailing blank lines.
1091             if ($CurrentSymbol ne "") {
1092                 $SymbolDoc =~ s/\s+$//;
1093                 $SymbolTypes{$CurrentSymbol} = $CurrentType;
1094                 $SymbolDocs{$CurrentSymbol} = $SymbolDoc;
1096                 if ($CurrentParam >= 0) {
1097                     $SymbolParams{$CurrentSymbol} = [ @Params ];
1098                 } else {
1099                     # Delete any existing params in case we are overriding a
1100                     # previously read template.
1101                     delete $SymbolParams{$CurrentSymbol};
1102                 }
1103             }
1104             $CurrentType = $type;
1105             $CurrentSymbol = $symbol;
1106             $CurrentParam = -1;
1107             $InUnusedParameters = 0;
1108             $SymbolDoc = "";
1109             @Params = ();
1111         } elsif (m/^<!-- # Unused Parameters # -->/) {
1112             $InUnusedParameters = 1;
1113             next;
1115         } else {
1116             # Workaround for an old bug that left a mess in the templates.
1117             # This happened with macros with args spread over several lines.
1118             if (m/^\@\\$/) {
1119               # Skip the next line.
1120               $_ = <DOCS>;
1121               next;
1122             }
1124             # Workaround for an old bug that left '@:' at start of lines.
1125             if (m/^\@:$/) {
1126               next;
1127             }
1130             # Check if param found. Need to handle "..." and "format...".
1131             if (s/^\@([\w\.]+):\040?//) {
1132                 my $param_name = $1;
1133                 # Allow variations of 'Returns'
1134                 if ($param_name =~ m/^[Rr]eturns?$/) {
1135                     $param_name = "Returns";
1136                 }
1137 #               print "Found param: $param_name\n";
1138                 push (@Params, $param_name);
1139                 push (@Params, $_);
1140                 $CurrentParam += 2;
1141                 next;
1142             }
1144             # When outputting the DocBook we skip unused parameters.
1145             if (!$InUnusedParameters || !$skip_unused_params) {
1146                 if ($CurrentParam >= 0) {
1147                     $Params[$CurrentParam] .= $_;
1148                 } else {
1149                     $SymbolDoc .= $_;
1150                 }
1151             }
1152         }
1153     }
1155     # Remember to finish the current symbol doccs.
1156     if ($CurrentSymbol ne "") {
1158         $SymbolDoc =~ s/\s+$//;
1159         $SymbolTypes{$CurrentSymbol} = $CurrentType;
1160         $SymbolDocs{$CurrentSymbol} = $SymbolDoc;
1162         if ($CurrentParam >= 0) {
1163             $SymbolParams{$CurrentSymbol} = [ @Params ];
1164         } else {
1165             delete $SymbolParams{$CurrentSymbol};
1166         }
1167     }
1169     close (DOCS);
1170     return 1;
1174 #############################################################################
1175 # Function    : ReadObjectHierarchy
1176 # Description : This reads in the $MODULE-hierarchy.txt file containing all
1177 #               the GtkObject subclasses described in this module (and their
1178 #               ancestors).
1179 #               It places them in the @Objects array, and places their level
1180 #               in the widget hierarchy in the @ObjectLevels array, at the
1181 #               same index. GtkObject, the root object, has a level of 1.
1183 #               FIXME: the version in gtkdoc-mkdb also generates tree_index.sgml
1184 #               as it goes along, this should be split out into a separate
1185 #               function.
1187 # Arguments   : none
1188 #############################################################################
1190 sub ReadObjectHierarchy {
1191     @Objects = ();
1192     @ObjectLevels = ();
1194     if (! -f $OBJECT_TREE_FILE) {
1195         return;
1196     }
1197     if (!open (INPUT, $OBJECT_TREE_FILE)) {
1198         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
1199         return;
1200     }
1201     while (<INPUT>) {
1202         if (m/\S+/) {
1203             my $object = $&;
1204             my $level = (length($`)) / 2 + 1;
1205 #            print ("Level: $level  Object: $object\n");
1207             push (@Objects, $object);
1208             push (@ObjectLevels, $level);
1209         }
1210     }
1212     close (INPUT);
1216 #############################################################################
1217 # Function    : ReadArgsFile
1218 # Description : This reads in an existing file which contains information on
1219 #               all GObject args. It creates the arrays @ArgObjects, @ArgNames,
1220 #               @ArgTypes and @ArgFlags containing info on the args.
1221 # Arguments   : $file - the file containing the arg information.
1222 #############################################################################
1224 sub ReadArgsFile {
1225     my ($file) = @_;
1227     my $in_arg = 0;
1228     my $arg_object;
1229     my $arg_name;
1230     my $arg_type;
1231     my $arg_flags;
1233     # Reset the signal info.
1234     @ArgObjects = ();
1235     @ArgNames = ();
1236     @ArgTypes = ();
1237     @ArgFlags = ();
1239     if (! -f $file) {
1240         return;
1241     }
1242     if (!open (INPUT, $file)) {
1243         warn "Can't open $file - skipping args\n";
1244         return;
1245     }
1246     while (<INPUT>) {
1247         if (!$in_arg) {
1248             if (m/^<ARG>/) {
1249                 $in_arg = 1;
1250                 $arg_object = "";
1251                 $arg_name = "";
1252                 $arg_type = "";
1253                 $arg_flags = "";
1254             }
1255         } else {
1256             if (m/^<NAME>(.*)<\/NAME>/) {
1257                 $arg_name = $1;
1258                 if ($arg_name =~ m/^(.*)::(.*)$/) {
1259                     $arg_object = $1;
1260                     ($arg_name = $2) =~ s/_/-/g;
1261 #                   print "Found arg: $arg_name\n";
1262                 } else {
1263                     print "Invalid arg name: $arg_name\n";
1264                 }
1265             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
1266                 $arg_type = $1;
1267             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
1268                 $arg_flags = $1;
1269             } elsif (m%^</ARG>%) {
1270 #               print "Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n";
1271                 push (@ArgObjects, $arg_object);
1272                 push (@ArgNames, $arg_name);
1273                 push (@ArgTypes, $arg_type);
1274                 push (@ArgFlags, $arg_flags);
1275                 $in_arg = 0;
1276             }
1277         }
1278     }
1279     close (INPUT);
1283 #############################################################################
1284 # Function    : CheckIsObject
1285 # Description : Returns 1 if the given name is a GObject or a subclass.
1286 #               It uses the global @Objects array.
1287 #               Note that the @Objects array only contains classes in the
1288 #               current module and their ancestors - not all GObject classes.
1289 # Arguments   : $name - the name to check.
1290 #############################################################################
1292 sub CheckIsObject {
1293     my ($name) = @_;
1295     my $object;
1296     foreach $object (@Objects) {
1297         if ($object eq $name) {
1298             return 1;
1299         }
1300     }
1301     return 0;