#!/usr/bin/perl -w # # gtk-doc - GTK DocBook documentation generator. # Copyright (C) 1998 Damon Chaplin # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################# # Script : gtkdoc-mkdb # Description : This creates the DocBook files from the edited templates. # # NOTE: When creating SGML IDS, we append -CAPS to all # all-caps identifiers to prevent name clashes. (It basically # never is the case that mixed-case identifiers would collide.) # See the CreateValidSGMLID function. ############################################################################# use strict; use Getopt::Long; # Options # name of documentation module my $MODULE; my $TMPL_DIR; my $SGML_OUTPUT_DIR; my @SOURCE_DIRS; my %optctl = (module => \$MODULE, 'source-dir' => \@SOURCE_DIRS, 'output-dir' => \$SGML_OUTPUT_DIR, 'tmpl-dir' => \$TMPL_DIR); GetOptions(\%optctl, "module=s", "source-dir:s", "output-dir:s"); my $ROOT_DIR = "."; # All the files are written in subdirectories beneath here. $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl"; # This is where we put all the DocBook output. $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/sgml"; # This file contains the object hierarchy. my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy"; # This file contains signal arguments and names. my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals"; # The file containing Arg information. my $ARGS_FILE = "$ROOT_DIR/$MODULE.args"; # These global arrays store information on signals. Each signal has an entry # in each of these arrays at the same index, like a multi-dimensional array. my @SignalObjects; # The GtkObject which emits the signal. my @SignalNames; # The signal name. my @SignalReturns; # The return type. my @SignalPrototypes; # The rest of the prototype of the signal handler. # These global arrays store information on Args. Each Arg has an entry # in each of these arrays at the same index, like a multi-dimensional array. my @ArgObjects; # The GtkObject which has the Arg. my @ArgNames; # The Arg name. my @ArgTypes; # The Arg type - gint, GtkArrowType etc. my @ArgFlags; # How the Arg can be used - readable/writable etc. # These global hashes store declaration info keyed on a symbol name. my %Declarations; my %DeclarationTypes; my %DeclarationConditional; my %DeclarationOutput; # These global hashes store the existing documentation. my %SymbolDocs; my %SymbolTypes; my %SymbolParams; # These global hashes store documentation scanned from the source files. my %SourceSymbolDocs; my %SourceSymbolParams; # These global arrays store GtkObject and subclasses and the hierarchy. my @Objects; my @ObjectLevels; # Create the root DocBook output directory if it doens't exist. if (! -e $SGML_OUTPUT_DIR) { mkdir ("$SGML_OUTPUT_DIR", 0777) || die "Can't create directory: $SGML_OUTPUT_DIR"; } # Function and other declaration output settings. my $RETURN_TYPE_FIELD_WIDTH = 12; my $SYMBOL_FIELD_WIDTH = 32; my $SIGNAL_FIELD_WIDTH = 12; &ReadSignalsFile ($SIGNALS_FILE); &ReadArgsFile ($ARGS_FILE); &ReadObjectHierarchy; # FIXME: this is the header file output at the top of the Synopsis. # We should allow this to be changed in the MODULE-sections.txt file. # gnome.h includes gtk/gtk.h which includes gdk/gdk.h which includes glib.h # so what should we output? - alternatives? my $HEADER_FILE = ""; if ($MODULE eq 'glib') { $HEADER_FILE = "glib.h"; } elsif ($MODULE eq 'gdk') { $HEADER_FILE = "gtk/gdk.h"; } elsif ($MODULE eq 'gtk') { $HEADER_FILE = "gtk/gtk.h"; } elsif ($MODULE eq 'gnome' || $MODULE eq 'gnomeui') { $HEADER_FILE = "gnome.h"; } for my $dir (@SOURCE_DIRS) { &ReadSourceDocumentation ($dir); } &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt"); ############################################################################# # Function : OutputObjectList # Description : This outputs the alphabetical list of objects, in a columned # table. FIXME: Currently this also outputs ancestor objects # which may not actually be in this module. # Arguments : none ############################################################################# sub OutputObjectList { my $cols = 3; open (OUTPUT, ">$SGML_OUTPUT_DIR/object_index.sgml") || die "Can't create $SGML_OUTPUT_DIR/object_index.sgml"; print (OUTPUT < EOF my $count = 0; my $object; foreach $object (sort(@Objects)) { my $xref = &MakeXRef ($object); if ($count % $cols == 0) { print (OUTPUT "\n"); } print (OUTPUT "$xref\n"); if ($count % $cols == ($cols - 1)) { print (OUTPUT "\n"); } $count++; } print (OUTPUT < EOF close (OUTPUT); } ############################################################################# # Function : OutputSGML # Description : This collects the output for each section of the docs, and # outputs each file when the end of the section is found. # Arguments : $file - the $MODULE-sections.txt file which contains all of # the functions/macros/structs etc. being documented, organised # into sections and subsections. ############################################################################# sub OutputSGML { my ($file) = @_; open (INPUT, $file) || die "Can't open $file"; my $book_top = ""; my $book_bottom = ""; my $includes = ""; my $section_includes = ""; my $in_section = 0; my $title = ""; my $subsection = ""; my $synopsis; my $details; my $num_symbols; while () { if (m/^#/) { next; } elsif (m/^
/) { $synopsis = ""; $details = ""; $num_symbols = 0; $in_section = 1; } elsif (m/^/i) { $synopsis .= "\n"; $subsection = $1; } elsif (m/^/) { } elsif (m/^(.*)<\/TITLE>/) { $title = $1; # print "Section: $title\n"; # We don't want warnings if object & class structs aren't used. $DeclarationOutput{$title} = 1; $DeclarationOutput{"${title}Class"} = 1; } elsif (m/^<FILE>(.*)<\/FILE>/) { $file = $1; %SymbolDocs = (); %SymbolTypes = (); %SymbolParams = (); &ReadTemplateFile ("$TMPL_DIR/$file.sgml", 1); &MergeSourceDocumentation; } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) { if ($in_section) { $section_includes = $1; } else { $includes = $1; } } elsif (m/^<\/SECTION>/) { if ($title eq "") { $title = $file; } # print "End of section: $title\n"; $file =~ s/\s/_/g; $file .= ".sgml"; # GtkObjects use their class name as the ID. my $section_id; if (&CheckIsObject ($title)) { $section_id = &CreateValidSGMLID ($title); } else { $section_id = &CreateValidSGMLID ("$MODULE-$title"); } if ($num_symbols > 0) { $book_top .= "<!entity $section_id SYSTEM \"sgml/$file\">\n"; $book_bottom .= " &$section_id;\n"; if ($section_includes eq "") { $section_includes = $includes; } &OutputSGMLFile ($file, $title, $section_id, $section_includes, \$synopsis, \$details); } $title = ""; $subsection = ""; $in_section = 0; $section_includes = ""; } elsif (m/^(\S+)/) { my $symbol = $1; #print " Symbol: $symbol\n"; my $declaration = $Declarations{$1}; if (defined ($declaration)) { # We don't want standard macros/functions of GtkObjects, # or private declarations. if ($subsection ne "Standard" && $subsection ne "Private") { my ($synop, $desc) = &OutputDeclaration ($symbol, $declaration); $synopsis .= $synop; $details .= $desc; } # Note that the declaration has been output. $DeclarationOutput{$symbol} = 1; } else { print "WARNING: No declaration for: $1\n"; } $num_symbols++; } } close (INPUT); &OutputBook ($book_top, $book_bottom); } ############################################################################# # Function : OutputDeclaration # Description : Returns the synopsis and detailed description DocBook # describing one function/macro etc. # Arguments : $symbol - the name of the function/macro begin described. # $declaration - the declaration of the function/macro. ############################################################################# sub OutputDeclaration { my ($symbol, $declaration) = @_; my $type = $DeclarationTypes {$symbol}; if ($type eq 'MACRO') { return &OutputMacro ($symbol, $declaration); } elsif ($type eq 'TYPEDEF') { return &OutputTypedef ($symbol, $declaration); } elsif ($type eq 'STRUCT') { return &OutputStruct ($symbol, $declaration); } elsif ($type eq 'ENUM') { return &OutputEnum ($symbol, $declaration); } elsif ($type eq 'UNION') { return &OutputUnion ($symbol, $declaration); } elsif ($type eq 'VARIABLE') { return &OutputVariable ($symbol, $declaration); } elsif ($type eq 'FUNCTION') { return &OutputFunction ($symbol, $declaration, $type); } elsif ($type eq 'USER_FUNCTION') { return &OutputFunction ($symbol, $declaration, $type); } else { die "Unknown symbol type"; } } ############################################################################# # Function : OutputMacro # Description : Returns the synopsis and detailed description of a macro. # Arguments : $symbol - the macro. # $declaration - the declaration of the macro. ############################################################################# sub OutputMacro { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $synop = "#define <link linkend=\"$id\">$symbol</link>"; my $desc; my $args = ""; if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) { $args = $1; if (length ($symbol) < $SYMBOL_FIELD_WIDTH) { $synop .= (' ' x ($SYMBOL_FIELD_WIDTH - length ($symbol))); } $synop .= &CreateValidSGML ($args); } $synop .= "\n"; if ($args ne "") { $desc = "<refsect2>\n<title><anchor id=\"$id\">${symbol}()\n"; } else { $desc = "\n<anchor id=\"$id\">$symbol\n"; } # Don't output the macro definition if is is a conditional macro or it # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is # longer than 2 lines, otherwise we get lots of complicated macros like # g_assert. if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/) && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) { $declaration = &CreateValidSGML ($declaration); $desc .= "$declaration\n"; } else { $desc .= "#define $symbol"; $desc .= &CreateValidSGML ($args); $desc .= "\n"; } if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } $desc .= &OutputParamDescriptions ("MACRO", $symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputTypedef # Description : Returns the synopsis and detailed description of a typedef. # Arguments : $symbol - the typedef. # $declaration - the declaration of the typedef, # e.g. 'typedef unsigned int guint;' ############################################################################# sub OutputTypedef { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $synop = "typedef $symbol;\n"; my $desc = "\n<anchor id=\"$id\">$symbol\n"; if (!defined ($DeclarationConditional{$symbol})) { $declaration = &CreateValidSGML ($declaration); $desc .= "$declaration\n"; } if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputStruct # Description : Returns the synopsis and detailed description of a struct. # We check if it is a widget struct, and if so we only output # parts of it that are noted as public fields. # We also use a different SGML ID for widget structs, since the # original ID is used for the entire RefEntry. # Arguments : $symbol - the struct. # $declaration - the declaration of the struct. ############################################################################# sub OutputStruct { my ($symbol, $declaration) = @_; my $is_widget_struct = 0; if (&CheckIsObject ($symbol)) { # print "Found widget struct: $symbol\n"; $is_widget_struct = 1; } my $id; if ($is_widget_struct) { $id = &CreateValidSGMLID ($symbol . "_struct"); } else { $id = &CreateValidSGMLID ($symbol); } my $synop = "struct $symbol;\n"; my $desc = "\n<anchor id=\"$id\">struct $symbol\n"; # Form a pretty-printed, private-data-removed form of the declaration my $decl_out; if ($declaration =~ m/^\s*$/) { # print "Found opaque struct\n"; $decl_out = "struct $symbol;"; } elsif ($is_widget_struct) { my $public = 0; my $new_declaration = ""; my $decl_line; foreach $decl_line (split (/\n/, $declaration)) { # print "Struct line: $decl_line\n"; if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) { $public = 1; } elsif ($decl_line =~ m%/\*\s*<\s*private\s*>\s*\*/%) { $public = 0; } elsif ($public) { $new_declaration .= $decl_line . "\n"; } } if ($new_declaration) { $decl_out = "struct $symbol {\n" . $new_declaration; # If we finished with public set, we already have the struct end. if ($public == 0) { $decl_out .= "};\n"; } } else { $decl_out = "struct $symbol;"; } } else { $decl_out = $declaration; } $decl_out = &CreateValidSGML ($decl_out); $desc .= "$decl_out\n"; if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } # Create a table of fields and descriptions # FIXME: Inserting  's into the produced type declarations here would # improve the output in most situations ... except for function # members of structs! my @fields = ParseStructDeclaration($declaration, $is_widget_struct, \&MakeXRef, sub { "$_[0]"; }); my $params = $SymbolParams{$symbol}; # If no parameters are filled in, we don't generate the description # table, for backwards compatibility my $found = 0; if (defined $params) { for (my $i = 1; $i <= $#$params; $i += 2) { if ($params->[$i] =~ /\S/) { $found = 1; last; } } } if ($found) { my %field_descrs = @$params; $desc .= < EOF while (@fields) { my $field_name = shift @fields; my $text = shift @fields; my $field_descr = $field_descrs{$field_name}; $desc .= "\n$text\n"; if (defined $field_descr) { $desc .= "".&ExpandAbbreviations($field_descr)."\n"; } else { $desc .= "\n"; } $desc .= "\n"; } $desc .= ""; } $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputEnum # Description : Returns the synopsis and detailed description of a enum. # Arguments : $symbol - the enum. # $declaration - the declaration of the enum. ############################################################################# sub OutputEnum { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $synop = "enum $symbol;\n"; my $desc = "\n<anchor id=\"$id\">enum $symbol\n"; $declaration = &CreateValidSGML ($declaration); $desc .= "$declaration\n"; if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } # Create a table of fields and descriptions my @members = ParseEnumDeclaration($declaration); my $params = $SymbolParams{$symbol}; # If no parameters are filled in, we don't generate the description # table, for backwards compatibility my $found = 0; if (defined $params) { for (my $i = 1; $i <= $#$params; $i += 2) { if ($params->[$i] =~ /\S/) { $found = 1; last; } } } if ($found) { my %member_descrs = @$params; $desc .= < EOF for my $member_name (@members) { my $member_descr = $member_descrs{$member_name}; $desc .= "\n$member_name\n"; if (defined $member_descr) { $desc .= "".&ExpandAbbreviations($member_descr)."\n"; } else { $desc .= "\n"; } $desc .= "\n"; } $desc .= ""; } $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputUnion # Description : Returns the synopsis and detailed description of a union. # Arguments : $symbol - the union. # $declaration - the declaration of the union. ############################################################################# sub OutputUnion { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $synop = "union $symbol;\n"; my $desc = "\n<anchor id=\"$id\">union $symbol\n"; $declaration = &CreateValidSGML ($declaration); $desc .= "$declaration\n"; if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputVariable # Description : Returns the synopsis and detailed description of a variable. # Arguments : $symbol - the extern'ed variable. # $declaration - the declaration of the variable. ############################################################################# sub OutputVariable { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $synop; if ($declaration =~ m/^\s*extern\s+((const\s+|unsigned\s+)*\w+)(\s+\*+|\*+|\s)(\s*)([A-Za-z]\w*)\s*;/) { my $mod = defined ($1) ? $1 : ""; my $ptr = defined ($3) ? $3 : ""; my $space = defined ($4) ? $4 : ""; $synop = "extern $mod$ptr$space$symbol;\n"; } else { $synop = "extern $symbol;\n"; } my $desc = "\n<anchor id=\"$id\">$symbol\n"; $declaration = &CreateValidSGML ($declaration); $desc .= "$declaration\n"; if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputFunction # Description : Returns the synopsis and detailed description of a function. # Arguments : $symbol - the function. # $declaration - the declaration of the function. ############################################################################# sub OutputFunction { my ($symbol, $declaration, $symbol_type) = @_; my $id = &CreateValidSGMLID ($symbol); # Take out the return type $declaration =~ s/\s*(const\s+|unsigned\s+)*(\w+)\s*(\**)\s*<\/RETURNS>\n//; my $type_modifier = defined($1) ? $1 : ""; my $type = $2; my $pointer = $3; my $xref = &MakeXRef ($type); my $start = ""; if ($symbol_type eq 'USER_FUNCTION') { # $start = "typedef "; } my $ret_type_len = length ($start) + length ($type_modifier) + length ($pointer) + length ($type); my $ret_type_output; my $symbol_len; if ($ret_type_len < $RETURN_TYPE_FIELD_WIDTH) { $ret_type_output = "$start$type_modifier$xref$pointer" . (' ' x ($RETURN_TYPE_FIELD_WIDTH - $ret_type_len)); $symbol_len = 0; } else { # $ret_type_output = "$start$type_modifier$xref$pointer\n" # . (' ' x $RETURN_TYPE_FIELD_WIDTH); $ret_type_output = "$start$type_modifier$xref$pointer "; $symbol_len = $ret_type_len + 1 - $RETURN_TYPE_FIELD_WIDTH; } $symbol_len += length ($symbol); my $char1 = my $char2 = my $char3 = ""; if ($symbol_type eq 'USER_FUNCTION') { $symbol_len += 3; $char1 = "("; $char2 = "*"; $char3 = ")"; } my ($symbol_output, $symbol_desc_output); if ($symbol_len < $SYMBOL_FIELD_WIDTH) { $symbol_output = "$char1$char2$symbol$char3" . (' ' x ($SYMBOL_FIELD_WIDTH - $symbol_len)); $symbol_desc_output = "$char1$char2$symbol$char3" . (' ' x ($SYMBOL_FIELD_WIDTH - $symbol_len)); } else { $symbol_output = "$char1$char2$symbol$char3\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)); $symbol_desc_output = "$char1$char2$symbol$char3\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)); } my $synop = $ret_type_output . $symbol_output . '('; my $desc = "\n<anchor id=\"$id\">${symbol} ()\n"; $desc .= "${ret_type_output}$symbol_desc_output("; my $param_num = 0; while ($declaration ne "") { if ($declaration =~ s/^[\s,]+//) { # skip whitespace and commas next; } elsif ($declaration =~ s/^void\s*[,\n]//) { $synop .= "void"; $desc .= "void"; } elsif ($declaration =~ s/^...\s*[,\n]//) { if ($param_num == 0) { $synop .= "..."; $desc .= "..."; } else { $synop .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " ..."; $desc .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " ..."; } # allow alphanumerics, '_', '[' & ']' in param names } elsif ($declaration =~ s/^(const\s+|unsigned\s+)*(struct\s+)?(\w+)\s*(\**)\s*(const\s+)?(\**)?\s*(\w+)?\s*(\[\d*\])?\s*[,\n]//) { my $mod1 = defined($1) ? $1 : ""; if (defined($2)) { $mod1 .= $2; } my $type = $3; my $ptr1 = $4; my $mod2 = defined($5) ? $5 : ""; my $ptr2 = $6; my $name = defined($7) ? $7 : ""; if ($name) { $ptr1 = " " . $ptr1; } my $array = defined($8) ? $8 : ""; my $xref = &MakeXRef ($type); # print "Type: $mod1$type $ptr1 $mod2 $name $array\n"; if ($param_num == 0) { $synop .= "$mod1$xref$ptr1$mod2$ptr2$name$array"; $desc .= "$mod1$xref$ptr1$mod2$ptr2$name$array"; } else { $synop .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " $mod1$xref$ptr1$mod2$ptr2$name$array"; $desc .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " $mod1$xref$ptr1$mod2$ptr2$name$array"; } # Try to match parameters which are functions. } elsif ($declaration =~ s/^(const\s+|unsigned\s+)*(struct\s+)?(\w+)\s*(\**)\s*(const\s+)?\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*[,\n]//) { my $mod1 = defined($1) ? $1 : ""; if (defined($2)) { $mod1 .= $2; } my $type = $3; my $ptr1 = $4; my $mod2 = defined($5) ? $5 : ""; my $name = $6; my $func_params = $7; my $xref = &MakeXRef ($type); # print "Type: $mod1$type$ptr1$mod2(*$name)($func_params)\n"; if ($param_num == 0) { $synop .= "$mod1$xref$ptr1$mod2 (*$name) ($func_params)"; $desc .= "$mod1$xref$ptr1$mod2 (*$name) ($func_params)"; } else { $synop .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " $mod1$xref$ptr1$mod2 (*$name) ($func_params)"; $desc .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " $mod1$xref$ptr1$mod2 (*$name) ($func_params)"; } } else { print "###Can't parse args for function $symbol: $declaration\n"; last; } $param_num++; } $synop .= ");\n"; $desc .= ");\n"; if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } $desc .= &OutputParamDescriptions ("FUNCTION", $symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputParamDescriptions # Description : Returns the DocBook output describing the parameters of a # function, macro or signal handler. # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal # handlers have an implicit user_data parameter last. # $symbol - the name of the function/macro being described. ############################################################################# sub OutputParamDescriptions { my ($symbol_type, $symbol) = @_; my $output = ""; if (defined ($SymbolParams{$symbol})) { my $returns = ""; my $params = $SymbolParams{$symbol}; my $params_desc = ""; if ($#$params < 0) { print "WARNING: 0 parameters\n"; } my $j; for ($j = 0; $j <= $#$params; $j += 2) { my $param_name = $$params[$j]; my $param = $$params[$j + 1]; if ($param_name eq "Returns") { $returns = &ExpandAbbreviations($param); } else { if ($param_name eq "Varargs") { $param_name = "..."; } $param = &ExpandAbbreviations($param); $params_desc .= "$param_name :\n$param\n"; } } # Signals have an implicit user_data parameter which we describe. if ($symbol_type eq "SIGNAL") { $params_desc .= "user_data :\nuser data set when the signal handler was connected.\n"; } # Start a table if we need one. if ($params_desc || $returns) { $output .= < EOF if ($params_desc ne "") { # $output .= "Parameters:\n"; $output .= $params_desc; } # Output the returns info last. if ($returns) { $output .= "Returns :$returns\n"; } # Finish the table. $output .= ""; } } return $output; } ############################################################################# # Function : OutputSGMLFile # Description : Outputs the final DocBook file for one section. # Arguments : $file - the name of the file. # $title - the title from the $MODULE-sections.txt file, which # will be overriden by the title in the template file. # $section_id - the SGML id to use for the toplevel tag. # $includes - comma-separates list of include files added at top # of synopsis, with '<' '>' around them. # $synopsis - reference to the DocBook for the Synopsis part. # $details - reference to the DocBook for the Details part. ############################################################################# sub OutputSGMLFile { my ($file, $title, $section_id, $includes, $synopsis, $details) = @_; # Find out if this is a GtkObject or descendant. my $signals_synop = ""; my $signals_desc = ""; my $args_synop = ""; my $args_desc = ""; my $hierarchy = ""; if (&CheckIsObject ($title)) { ($signals_synop, $signals_desc) = &GetSignals ($title); ($args_synop, $args_desc) = &GetArgs ($title); $hierarchy = &GetHierarchy ($title); } # The edited title overrides the one from the sections file. my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"}; if (defined ($new_title) && $new_title !~ m/^\s*$/) { $title = $new_title; # print "Found title: $title\n"; } my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"}; if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) { # $short_desc = "one line description goes here."; $short_desc = ""; } else { $short_desc = &ExpandAbbreviations($short_desc); # print "Found short_desc: $short_desc"; } my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"}; if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) { $long_desc = "\nA longer description goes here.\n\n"; } else { $long_desc = &ExpandAbbreviations($long_desc); # print "Found long_desc: $long_desc"; } my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"}; if (!defined ($see_also) || $see_also =~ m%^\s*()?\s*()?\s*$%) { $see_also = ""; } else { $see_also = &ExpandAbbreviations($see_also); # print "Found see_also: $see_also"; } if ($see_also) { $see_also = "\nSee Also\n$see_also\n\n"; } my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime (time); my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon]; $year += 1900; my $include_output = ""; my $include; foreach $include (split (/,/, $includes)) { $include_output .= "#include <${include}>\n"; } open (OUTPUT, ">$SGML_OUTPUT_DIR/$file") || die "Can't create $SGML_OUTPUT_DIR/$file"; # Note: The refname and refpurpose are on the same line to stop # docbook-to-man 1.08 putting them on separate lines. print OUTPUT < $title 3 \U$MODULE\E Library $title$short_desc Synopsis $include_output $${synopsis} $hierarchy $args_synop $signals_synop Description $long_desc Details $$details $args_desc $signals_desc $see_also EOF close (OUTPUT); } ############################################################################# # Function : OutputBook # Description : Outputs the SGML entities that need to be included into the # main SGML file for the module. # Arguments : $book_top - the declarations of the entities, which are added # at the top of the main SGML file. # $book_bottom - the references to the entities, which are # added in the main SGML file at the desired position. ############################################################################# sub OutputBook { my ($book_top, $book_bottom) = @_; open (OUTPUT, ">$SGML_OUTPUT_DIR/$MODULE-doc.top") || die "Can't create $SGML_OUTPUT_DIR/$MODULE-doc.top"; print OUTPUT $book_top; close (OUTPUT); open (OUTPUT, ">$SGML_OUTPUT_DIR/$MODULE-doc.bottom") || die "Can't create $SGML_OUTPUT_DIR/$MODULE-doc.bottom"; print OUTPUT $book_bottom; close (OUTPUT); } ############################################################################# # Function : CreateValidSGMLID # Description : Creates a valid SGML 'id' from the given string. # NOTE: SGML ids are case-insensitive, so we have a few special # cases to avoid clashes of ids. # Arguments : $id - the string to be converted into a valid SGML id. ############################################################################# sub CreateValidSGMLID { my ($id) = $_[0]; # Append -CAPS to all all-caps identifiers # Special case, '_' would end up as '' so we use 'gettext-macro' instead. if ($id eq "_") { return "gettext-macro"; } if ($id !~ /[a-z]/) { $id .= "-CAPS" }; $id =~ s/[_ ]/-/g; $id =~ s/[,\.]//g; $id =~ s/^-*//; $id =~ s/::/-/g; return $id; } ############################################################################# # Function : CreateValidSGML # Description : This turns any chars which are used in SGML into entities, # e.g. '<' into '<' # Arguments : $text - the text to turn into proper SGML. ############################################################################# sub CreateValidSGML { my ($text) = @_; $text =~ s/&/&/g; # Do this first, or the others get messed up. $text =~ s//>/g; return $text; } ############################################################################# # Function : ExpandAbbreviations # Description : This turns the abbreviations function(), macro(), @param, # %constant, and #symbol into appropriate DocBook markup. # Arguments : $text - the text to expand. ############################################################################# sub ExpandAbbreviations { my ($text) = @_; # Convert 'function()' or 'macro()' $text =~ s/(\w+)\s*\(\)/&MakeXRef($1) . "()";/eg; # Convert '@param' $text =~ s/\@(\w+)/$1<\/parameter>/g; # Convert '%constant'. Also allow negative numbers, e.g. %-1. $text =~ s/\%(-?\w+)/$1<\/literal>/g; # Convert '#symbol' $text =~ s/#([\w-]+)/&MakeXRef($1);/eg; return $text; } ############################################################################# # Function : MakeXRef # Description : This returns a cross-reference link to the given symbol. # Though it doesn't try to do this for a few standard C types # that it knows won't be in the documentation. # Arguments : $symbol - the symbol to try to create a XRef to. ############################################################################# sub MakeXRef { my ($symbol) = $_[0]; # print "Getting type link for $symbol\n"; # Don't create a link for some standard C types and functions, to cut # down on the number of warnings output by jade. if ($symbol eq "void" || $symbol eq "va_list" || $symbol eq "int" || $symbol eq "char" || $symbol eq "printf" || $symbol eq "sprintf") { return $symbol; } my $symbol_id = &CreateValidSGMLID ($symbol); # Get rid of special '-struct' suffix. $symbol =~ s/-struct$//; return "$symbol"; } ############################################################################# # Function : GetHierarchy # Description : Returns the DocBook output describing the ancestors of a # GtkObject subclass. It uses the global @Objects and # @ObjectLevels arrays to walk up the tree. # Arguments : $object - the GtkObject subclass. ############################################################################# sub GetHierarchy { my ($object) = @_; # Find object in the objects array. my $found = 0; my $i; for ($i = 0; $i < @Objects; $i++) { if ($Objects[$i] eq $object) { $found = 1; last; } } if (!$found) { return ""; } # Walk up the hierarchy, pushing ancestors onto the ancestors array. my @ancestors = (); push (@ancestors, $object); my $level = $ObjectLevels[$i]; # print "Level: $level\n"; while ($level > 1) { $i--; if ($ObjectLevels[$i] < $level) { push (@ancestors, $Objects[$i]); $level = $ObjectLevels[$i]; # print "Level: $level\n"; } } # Output the ancestors list, indented and with links. my $hierarchy = "\n\n"; $level = 0; for ($i = $#ancestors; $i >= 0; $i--) { my $link_text; # Don't add a link to the current widget, i.e. when i == 0. if ($i > 0) { my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]); $link_text = "$ancestors[$i]"; } else { $link_text = "$ancestors[$i]"; } if ($level == 0) { $hierarchy .= " $link_text\n"; } else { # $hierarchy .= ' ' x ($level * 6 - 3) . "|\n"; $hierarchy .= ' ' x ($level * 6 - 3) . "+----$link_text\n"; } $level++; } $hierarchy .= "\n"; return < Object Hierarchy $hierarchy EOF } ############################################################################# # Function : GetSignals # Description : Returns the synopsis and detailed description DocBook output # for the signal handlers of a given GtkObject subclass. # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'. ############################################################################# sub GetSignals { my ($object) = @_; my $synop = ""; my $desc = ""; my $i; for ($i = 0; $i <= $#SignalObjects; $i++) { if ($SignalObjects[$i] eq $object) { # print "Found signal: $SignalNames[$i]\n"; my $name = $SignalNames[$i]; my $symbol = "${object}::${name}"; my $id = &CreateValidSGMLID ("$object-$name"); my $name_len = length ($name) + 2; if ($name_len < $SIGNAL_FIELD_WIDTH) { $synop .= ""$name"" . (' ' x ($SIGNAL_FIELD_WIDTH - $name_len)); } else { $synop .= ""$name"\n" . (' ' x $SIGNAL_FIELD_WIDTH); } $desc .= "<anchor id=\"$id\">The "$name" signal\n"; $desc .= ""; $SignalReturns[$i] =~ m/\s*(const\s*)?(\w+)\s*(\**)/; my $type_modifier = defined($1) ? $1 : ""; my $type = $2; my $pointer = $3; my $xref = &MakeXRef ($type); my $ret_type_len = length ($type_modifier) + length ($pointer) + length ($type); my $ret_type_output = "$type_modifier$xref$pointer" . (' ' x ($RETURN_TYPE_FIELD_WIDTH - $ret_type_len)); $synop .= "${ret_type_output}user_function ("; $desc .= "${ret_type_output}user_function ("; my @params = split ("\n", $SignalPrototypes[$i]); my $j; for ($j = 0; $j <= $#params; $j++) { # allow alphanumerics, '_', '[' & ']' in param names if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) { $type = $1; $pointer = $2; $name = $3; $xref = &MakeXRef ($type); $synop .= "$xref $pointer$name,\n"; $synop .= (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)); $desc .= "$xref $pointer$name,\n"; $desc .= (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)); } else { print "###Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]\n"; } } $xref = &MakeXRef ("gpointer"); $synop .= "$xref user_data);\n"; $desc .= "$xref user_data);\n"; if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } $desc .= &OutputParamDescriptions ("SIGNAL", $symbol); $desc .= ""; } } if ($synop ne '') { $synop = < Signal Prototypes ${synop} EOF $desc = < Signals $desc EOF } return ($synop, $desc); } ############################################################################# # Function : GetArgs # Description : Returns the synopsis and detailed description DocBook output # for the Args of a given GtkObject subclass. # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'. ############################################################################# sub GetArgs { my ($object) = @_; my $synop = ""; my $desc = ""; my $i; for ($i = 0; $i <= $#ArgObjects; $i++) { if ($ArgObjects[$i] eq $object) { # print "Found arg: $ArgNames[$i]\n"; my $name = $ArgNames[$i]; # Remember only one colon so we don't clash with signals. my $symbol = "${object}:${name}"; # I've used two dashes here for the same reason. my $id = &CreateValidSGMLID ("$object--$name"); my $type = $ArgTypes[$i]; my $type_output; if ($type eq "GtkSignal") { $type = "GtkSignalFunc, gpointer"; $type_output = &MakeXRef ("GtkSignalFunc") . ", " . &MakeXRef ("gpointer"); } elsif ($type eq "GtkString") { $type = "gchar*"; $type_output = &MakeXRef ("gchar") . "*"; } else { $type_output = &MakeXRef ($type); } my $flags = $ArgFlags[$i]; my $flags_string = ""; if ($flags =~ m/r/) { $flags_string = "Read"; } if ($flags =~ m/w/) { if ($flags_string) { $flags_string .= " / "; } $flags_string .= "Write"; } if ($flags =~ m/x/) { if ($flags_string) { $flags_string .= " / "; } $flags_string .= "Construct"; } if ($flags =~ m/X/) { if ($flags_string) { $flags_string .= " / "; } $flags_string .= "Construct Only"; } if ($flags =~ m/c/) { if ($flags_string) { $flags_string .= " / "; } $flags_string .= "Child"; } my $pad1 = " " x (20 - length ($name)); my $pad2 = " " x (20 - length ($type)); $synop .= " "$name"$pad1 $type_output$pad2 : $flags_string\n"; $desc .= ""$name" ($type_output : $flags_string)\n\n"; if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($SymbolDocs{$symbol}); } else { $desc .= "\n"; } $desc .= "\n"; } } if ($synop ne '') { $synop = < Args ${synop} EOF $desc = < Args $desc EOF } return ($synop, $desc); } ############################################################################# # Function : ReadSourceDocumentation # Description : This reads in the documentation embedded in comment blocks # in the source code (for Gnome). # # Parameter descriptions override any in the template files. # Function descriptions are placed before any description from # the template files. # # It recursively descends the source directory looking for .c # files and scans them looking for specially-formatted comment # blocks. # # Arguments : $source_dir - the directory to scan. ############################################################################# sub ReadSourceDocumentation { my ($source_dir) = @_; # print "Scanning source directory: $source_dir\n"; # This array holds any subdirectories found. my (@subdirs) = (); opendir (SRCDIR, $source_dir) || die "Can't open source directory $source_dir: $!"; my $file; foreach $file (readdir (SRCDIR)) { if ($file =~ /^\./) { next; } elsif (-d "$source_dir/$file") { push (@subdirs, $file); } elsif ($file =~ m/\.c$/) { &ScanSourceFile ("$source_dir/$file"); } } closedir (SRCDIR); # Now recursively scan the subdirectories. my $dir; foreach $dir (@subdirs) { &ReadSourceDocumentation ("$source_dir/$dir"); } } ############################################################################# # Function : ScanSourceFile # Description : Scans one source file looking for specially-formatted comment # blocks. It calls &MergeSourceDocumentation to merge any # documentation found with the documentation already read in # from the template files. # # Arguments : $file - the file to scan. ############################################################################# sub ScanSourceFile { my ($file) = @_; # print "Scanning source file: $file\n"; open (SRCFILE, $file) || die "Can't open $file: $!"; my $in_comment_block = 0; my $symbol; my ($in_description, $in_return); my ($description, $return_desc, $return_start); my $current_param; my @params; while () { # Look for the start of a comment block. if (!$in_comment_block) { if (m%^\s*/\*\*\s%) { # print "Found comment block start\n"; $in_comment_block = 1; # Reset all the symbol data. $symbol = ""; $in_description = 0; $in_return = 0; $description = ""; $return_desc = ""; $current_param = -1; @params = (); } next; } # We're in a comment block. Check if we've found the end of it. if (m%^\s*\*+/%) { # print "Found comment block end: $symbol\n"; if (!$symbol) { print <\n\n%g) { # print "Converted blank lines:\n$src_doc\n"; } $SymbolDocs{$symbol} = "\n$src_doc\n$tmpl_doc"; # The templates contain the definitive parameter names and order, # so we will not change that. We only override the actual text. my $tmpl_params = $SymbolParams{$symbol}; if (!defined ($tmpl_params)) { next; } my $params = $SourceSymbolParams{$symbol}; my $j; for ($j = 0; $j <= $#$tmpl_params; $j += 2) { my $tmpl_param_name = $$tmpl_params[$j]; my $tmpl_param_desc = $$tmpl_params[$j + 1]; # Try to find the param in the source comment documentation. my $found = 0; my $k; for ($k = 0; $k <= $#$params; $k += 2) { my $param_name = $$params[$k]; my $param_desc = $$params[$k + 1]; # We accept changed in case, since the Gnome source docs # contain a lot of these. if ("\L$param_name" eq "\L$tmpl_param_name") { $found = 1; # Override the description. $$tmpl_params[$j + 1] = &CreateValidSGML ($param_desc); # Set the name to "" to mark it as used. $$params[$k] = ""; last; } } # Output a warning if the parameter is not found. if (!$found) { print <) { if (!$declaration_type) { if (m/^<([^>]+)>/) { $declaration_type = $1; $declaration_name = ""; # print "Found declaration: $declaration_type\n"; $declaration = ""; } } else { if (m%^(.*)%) { $declaration_name = $1; } elsif (m%^%) { # print "Found end of declaration: $declaration_name\n"; # Check that the declaration has a name if ($declaration_name eq "") { print "ERROR: $declaration_type has no name $file:$.\n"; } # Check if the symbol is already defined. if (defined ($Declarations{$declaration_name}) && $override == 0) { # Function declarations take precedence. if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') { # Ignore it. } elsif ($declaration_type eq 'FUNCTION') { $Declarations{$declaration_name} = $declaration; $DeclarationTypes{$declaration_name} = $declaration_type; } elsif ($DeclarationTypes{$declaration_name} eq $declaration_type) { # If the existing declaration is empty override it. if ($declaration_type eq 'STRUCT') { if ($Declarations{$declaration_name} =~ m/^\s*$/) { $Declarations{$declaration_name} = $declaration; } elsif ($declaration =~ m/^\s*$/) { # Ignore an empty declaration. } else { print "WARNING: Structure has multiple definitions: $declaration_name\n"; } } else { # set flag in %DeclarationConditional hash for # multiply defined macros/typedefs. $DeclarationConditional{$declaration_name} = 1; } } else { print "ERROR: $declaration_name has multiple definitions\n"; } } else { $Declarations{$declaration_name} = $declaration; $DeclarationTypes{$declaration_name} = $declaration_type; } $declaration_type = ""; } else { $declaration .= $_; } } } close (INPUT); } ############################################################################# # Function : ReadSignalsFile # Description : This reads in an existing file which contains information on # all GTK signals. It creates the arrays @SignalNames and # @SignalPrototypes containing info on the signals. The first # line of the SignalPrototype is the return type of the signal # handler. The remaining lines are the parameters passed to it. # The last parameter, "gpointer user_data" is always the same # so is not included. # Arguments : $file - the file containing the signal handler prototype # information. ############################################################################# sub ReadSignalsFile { my ($file) = @_; my $in_signal = 0; my $signal_object; my $signal_name; my $signal_returns; my $signal_prototype; # Reset the signal info. @SignalObjects = (); @SignalNames = (); @SignalReturns = (); @SignalPrototypes = (); if (! -f $file) { return; } if (!open (INPUT, $file)) { warn "Can't open $file - skipping signals\n"; return; } while () { if (!$in_signal) { if (m/^/) { $in_signal = 1; $signal_object = ""; $signal_name = ""; $signal_returns = ""; $signal_prototype = ""; } } else { if (m/^(.*)<\/NAME>/) { $signal_name = $1; if ($signal_name =~ m/^(.*)::(.*)$/) { $signal_object = $1; $signal_name = $2; # print "Found signal: $signal_name\n"; } else { print "Invalid signal name: $signal_name\n"; } } elsif (m/^(.*)<\/RETURNS>/) { $signal_returns = $1; } elsif (m%^%) { # print "Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}"; push (@SignalObjects, $signal_object); push (@SignalNames, $signal_name); push (@SignalReturns, $signal_returns); push (@SignalPrototypes, $signal_prototype); $in_signal = 0; } else { $signal_prototype .= $_; } } } close (INPUT); } ############################################################################# # Function : ReadTemplateFile # Description : This reads in the manually-edited documentation file # corresponding to the file currently being created, so we can # insert the documentation at the appropriate places. # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which # is a hash of arrays. # NOTE: This function is duplicated in gtkdoc-mkdb (but # slightly different). # Arguments : $docsfile - the template file to read in. # $skip_unused_params - 1 if the unused parameters should be # skipped. ############################################################################# sub ReadTemplateFile { my ($docsfile, $skip_unused_params) = @_; # print "Reading $docsfile\n"; if (! -f $docsfile) { print "File doesn't exist: $docsfile\n"; return; } my $current_type = ""; # Type of symbol being read. my $current_symbol = ""; # Name of symbol being read. my $symbol_doc = ""; # Description of symbol being read. my @params; # Parameter names and descriptions of current # function/macro/function typedef. my $current_param = -1; # Index of parameter currently being read. # Note that the param array contains pairs # of param name & description. my $in_unused_params = 0; # True if we are reading in the unused params. open (DOCS, $docsfile) || die "Can't open file $docsfile: $!"; while () { if (m/^/) { my $type = $1; my $symbol = $2; if ($symbol eq "Title" || $symbol eq "Short_Description" || $symbol eq "Long_Description" || $symbol eq "See_Also") { $symbol = $docsfile . ":" . $symbol; # print "Found symbol: $symbol\n"; } # Store previous symbol, but remove any trailing blank lines. if ($current_symbol ne "") { $symbol_doc =~ s/\s+$//; $SymbolTypes{$current_symbol} = $current_type; $SymbolDocs{$current_symbol} = $symbol_doc; if ($current_param >= 0) { $SymbolParams{$current_symbol} = [ @params ]; } else { # Delete any existing params in case we are overriding a # previously read template. delete $SymbolParams{$current_symbol}; } } $current_type = $type; $current_symbol = $symbol; $current_param = -1; $in_unused_params = 0; $symbol_doc = ""; @params = (); } elsif (m/^/) { # print "DEBUG: Found unused parameters\n"; $in_unused_params = 1; next; } elsif ($in_unused_params && $skip_unused_params) { # When outputting the DocBook we skip unused parameters. # print "DEBUG: Skipping unused param: $_"; next; } else { # Check if param found if (s/^\@(\S+):\s*//) { my $param_name = $1; # Allow variations of 'Returns' if ($param_name =~ m/^[Rr]eturns?$/) { $param_name = "Returns"; } # print "Found param: $param_name\n"; push (@params, $param_name); push (@params, $_); $current_param += 2; next; } if ($current_param >= 0) { $params[$current_param] .= $_; } else { $symbol_doc .= $_; } } } # Remember to finish the current symbol doccs. if ($current_symbol ne "") { $symbol_doc =~ s/\s+$//; $SymbolTypes{$current_symbol} = $current_type; $SymbolDocs{$current_symbol} = $symbol_doc; if ($current_param >= 0) { $SymbolParams{$current_symbol} = [ @params ]; } else { delete $SymbolParams{$current_symbol}; } } close (DOCS); } ############################################################################# # Function : ReadObjectHierarchy # Description : This reads in the $MODULE-hierarchy.txt file containing all # the GtkObject subclasses described in this module (and their # ancestors). # It places them in the @Objects array, and places their level # in the widget hierarchy in the @ObjectLevels array, at the # same index. GtkObject, the root object, has a level of 1. # # FIXME: the version in gtkdoc-mkdb also generates tree_index.sgml # as it goes along, this should be split out into a separate # function. # # Arguments : none ############################################################################# sub ReadObjectHierarchy { @Objects = (); @ObjectLevels = (); if (! -f $OBJECT_TREE_FILE) { return; } if (!open (INPUT, $OBJECT_TREE_FILE)) { warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n"; return; } open (OUTPUT, ">$SGML_OUTPUT_DIR/tree_index.sgml") || die "Can't create $SGML_OUTPUT_DIR/tree_index.sgml"; print (OUTPUT "\n"); while () { if (m/\S+/) { my $object = $&; my $level = (length($`)) / 2 + 1; # print ("Level: $level Object: $object\n"); my $xref = &MakeXRef ($object); print (OUTPUT ' ' x ($level * 4), "$xref\n"); push (@Objects, $object); push (@ObjectLevels, $level); } } print (OUTPUT "\n"); close (INPUT); close (OUTPUT); &OutputObjectList; } ############################################################################# # Function : ReadArgsFile # Description : This reads in an existing file which contains information on # all GTK args. It creates the arrays @ArgObjects, @ArgNames, # @ArgTypes and @ArgFlags containing info on the args. # Arguments : $file - the file containing the arg information. ############################################################################# sub ReadArgsFile { my ($file) = @_; my $in_arg = 0; my $arg_object; my $arg_name; my $arg_type; my $arg_flags; # Reset the signal info. @ArgObjects = (); @ArgNames = (); @ArgTypes = (); @ArgFlags = (); if (! -f $file) { return; } if (!open (INPUT, $file)) { warn "Can't open $file - skipping args\n"; return; } while () { if (!$in_arg) { if (m/^/) { $in_arg = 1; $arg_object = ""; $arg_name = ""; $arg_type = ""; $arg_flags = ""; } } else { if (m/^(.*)<\/NAME>/) { $arg_name = $1; if ($arg_name =~ m/^(.*)::(.*)$/) { $arg_object = $1; $arg_name = $2; # print "Found arg: $arg_name\n"; } else { print "Invalid arg name: $arg_name\n"; } } elsif (m/^(.*)<\/TYPE>/) { $arg_type = $1; } elsif (m/^(.*)<\/FLAGS>/) { $arg_flags = $1; } elsif (m%^%) { # print "Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n"; push (@ArgObjects, $arg_object); push (@ArgNames, $arg_name); push (@ArgTypes, $arg_type); push (@ArgFlags, $arg_flags); $in_arg = 0; } } } close (INPUT); } ############################################################################# # Function : CheckIsObject # Description : Returns 1 if the given name is a GtkObject or a subclass. # It uses the global @Objects array. # Note that the @Objects array only contains classes in the # current module and their ancestors - not all GTK classes. # Arguments : $name - the name to check. ############################################################################# sub CheckIsObject { my ($name) = @_; my $object; foreach $object (@Objects) { if ($object eq $name) { return 1; } } return 0; } ############################################################################# # Function : ParseStructDeclaration # Description : This function takes a structure declaration and # breaks it into individual type declarations. # Arguments : $declaration - the declaration to parse # $is_object - true if this is an object structure # $typefunc - function reference to apply to type # $namefunc - function reference to apply to name ############################################################################# sub ParseStructDeclaration { my ($declaration, $is_object, $typefunc, $namefunc) = @_; # Remove all private parts of the declaration # For objects, assume private if ($is_object) { $declaration =~ s!(struct\s+\w*\s*\{) .*? (?:/\*\s*<\s*public\s*>\s*\*/|(?=\}))!$1!msgx; } $declaration =~ s!\n?[ \t]*/\*\s*<\s*private\s*>\s*\*/ .*? (?:/\*\s*<\s*public\s*>\s*\*/|(?=\}))!!msgx; # Remove all other comments; $declaration =~ s@/\*([^*]+|\*(?!/))*\*/@ @g; my @result = (); if ($declaration =~ /^\s*$/) { return @result; } # Prime match after "struct {" declaration if (!scalar($declaration =~ m/struct\s+\w*\s*\{/msg)) { die "Structure declaration '$declaration' does not begin with struct [NAME] {\n"; } # Treat lines in sequence, allowing singly nested anonymous structs # and unions. while ($declaration =~ m/\s*([^{;]+(\{[^\}]*\}[^{;]+)?);/msg) { my $line = $1; last if $line =~ /^\s*\}\s*\w*\s*$/; # FIXME: Just ignore nested structs and unions for now next if $line =~ /{/; # FIXME: The regexes here are the same as in OutputFunction; # this functionality should be separated out. if ($line =~ m/^ (const\s+|unsigned\s+)*(struct\s+)? # mod1 (\w+)\s* # type (\**)\s* # ptr1 (const\s+)? # mod2 (\**)?\s* # ptr2 (\w+(?:\s*,\s*\w+)*)\s* # name (?:((?:\[[^\]]*\]\s*)+) | # array (:\s*\d+))?\s* # bits $/x) { my $mod1 = defined($1) ? $1 : ""; if (defined($2)) { $mod1 .= $2; } my $type = $3; my $ptr1 = $4; my $mod2 = defined($5) ? $5 : ""; my $ptr2 = $6; my $name = $7; $ptr1 = " " . $ptr1; my $array = defined($8) ? $8 : ""; my $bits = defined($9) ? " $9" : ""; my $ptype = defined $typefunc ? $typefunc->($type) : $type; # FIXME: # As a hack, we allow the "name" to be of the form # "a, b, c". This isn't the correct C syntax, but # at least we get "gint16 x, y" right. Such constructs # should really be completely removed from the source. # Or we should really try to understand the C syntax # here... my @names = split /\s*,\s*/, $name; for my $n (@names) { push @result, $n; if (defined $namefunc) { $n = $namefunc->($n); } push @result, "$mod1$ptype$ptr1$mod2$ptr2$n$array$bits"; } # Try to match structure members which are functions } elsif ($line =~ m/^ (const\s+|unsigned\s+)*(struct\s+)? # mod1 (\w+)\s* # type (\**)\s* # ptr1 (const\s+)? # mod2 \(\s*\*\s*(\w+)\s*\)\s* # name \(([^)]*)\)\s* # func_params $/x) { my $mod1 = defined($1) ? $1 : ""; if (defined($2)) { $mod1 .= $2; } my $type = $3; my $ptr1 = $4; my $mod2 = defined($5) ? $5 : ""; my $name = $6; my $func_params = $7; my $ptype = defined $typefunc ? $typefunc->($type) : $type; my $pname = defined $namefunc ? $namefunc->($name) : $name; push @result, $name; push @result, "$mod1$ptype$ptr1$mod2 (*$pname) ($func_params)"; } else { warn "Cannot parse structure field $line"; } } return @result; } ############################################################################# # Function : ParseEnumDeclaration # Description : This function takes a enumeration declaration and # breaks it into individual enum member declarations. # Arguments : $declaration - the declaration to parse ############################################################################# sub ParseEnumDeclaration { my ($declaration, $is_object) = @_; # Remove comments; $declaration =~ s@/\*([^*]+|\*(?!/))*\*/@ @g; my @result = (); if ($declaration =~ /^\s*$/) { return @result; } # Remove parenthesized expressions (in macros like GTK_BLAH = BLAH(1,3)) # to avoid getting confused by commas they might contain. This # doesn't handle nested parentheses correctly. $declaration =~ s/\([^)]+\)//g; # Prime match after "typedef enum {" declaration if (!scalar($declaration =~ m/typedef\s+enum\s*\{/msg)) { die "Enum declaration '$declaration' does not begin with typedef enum {\n"; } # Treat lines in sequence. while ($declaration =~ m/\s*([^,\}]+)([,\}])/msg) { my $line = $1; my $terminator = $2; if ($line =~ m/^(\w+)\s*(=.*)?$/msg) { push @result, $1; # Special case for GIOCondition, where the values are specified by # macros which expand to include the equal sign like '=1'. } elsif ($line =~ m/^(\w+)\s*GLIB_SYSDEF_POLL/msg) { push @result, $1; # Special case include of , just ignore it } elsif ($line =~ m/^#include/) { last; } else { warn "Cannot parse enumeration member $line"; } last if $terminator eq '}'; } return @result; }