4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 2001 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.
23 # These are functions used by several of the gtk-doc Perl scripts.
24 # We'll move more of the common routines here eventually, though they need to
25 # stop using global variables first.
31 #############################################################################
32 # Function : UpdateFileIfChanged
33 # Description : Compares the old version of the file with the new version and
34 # if the file has changed it moves the new version into the old
35 # versions place. This is used so we only change files if
36 # needed, so we can do proper dependency tracking and we don't
37 # needlessly check files into version control systems that haven't
39 # It returns 0 if the file hasn't changed, and 1 if it has.
40 # Arguments : $old_file - the pathname of the old file.
41 # $new_file - the pathname of the new version of the file.
42 # $make_backup - 1 if a backup of the old file should be kept.
43 # It will have the .bak suffix added to the file name.
44 #############################################################################
46 sub UpdateFileIfChanged
{
47 my ($old_file, $new_file, $make_backup) = @_;
49 #@TRACE@("Comparing $old_file with $new_file...");
51 # If the old file doesn't exist we want this to default to 1.
55 `cmp -s "$old_file" "$new_file"`;
57 #@TRACE@(" cmp exit code: $exit_code ($?)");
61 die "Error running 'cmp $old_file $new_file'";
64 if ($exit_code == 1) {
65 #@TRACE@(" files changed - replacing old version with new version.");
66 if ($make_backup && -e
$old_file) {
67 rename ($old_file, "$old_file.bak")
68 || die "Can't move $old_file to $old_file.bak: $!";
70 rename ($new_file, $old_file)
71 || die "Can't move $new_file to $old_file: $!";
75 #@TRACE@(" files the same - deleting new version.");
77 || die "Can't delete file: $new_file: $!";
84 #############################################################################
85 # Function : ParseStructDeclaration
86 # Description : This function takes a structure declaration and
87 # breaks it into individual type declarations.
88 # Arguments : $declaration - the declaration to parse
89 # $is_object - true if this is an object structure
90 # $output_function_params - true if full type is wanted for
91 # function pointer members
92 # $typefunc - function reference to apply to type
93 # $namefunc - function reference to apply to name
94 #############################################################################
96 sub ParseStructDeclaration
{
97 my ($declaration, $is_object, $output_function_params, $typefunc, $namefunc) = @_;
99 # For forward struct declarations just return an empty array.
100 if ($declaration =~ m/(?:struct|union)\s+\S+\s*;/msg) {
104 # Remove all private parts of the declaration
106 # For objects, assume private
108 $declaration =~ s
!((?
:struct
|union
)\s
+\w
*\s
*\
{)
110 (?
:/\*\s*<\s*public\s*>\s*\*/|(?
=\
}))!$1!msgx
;
113 # Remove private symbols
114 # Assume end of declaration if line begins with '}'
115 $declaration =~ s!\n?[ \t]*/\*\s*<\s*(private|protected)\s*>\s*\*/.*?(?:/\*\s*<\s*public\s*>\s*\*/|(?=^\}))!!msgx;
117 # Remove all other comments
118 $declaration =~ s@
\n\s
*/\*([^*]+|\*(?!/))*\
*/\s
*\n@
\n@msg;
119 $declaration =~ s@
/\*([^*]+|\*(?!/))*\
*/@
@g;
120 $declaration =~ s@
\n\s
*//.*?
\n@
\n@msg;
121 $declaration =~ s@
//.*@
@g;
123 # Remove g_iface, parent_instance and parent_class if they are first member
124 $declaration =~ s/(\{)\s*(\w)+\s+(g_iface|parent_instance|parent_class)\s*;/$1/g;
128 if ($declaration =~ /^\s*$/) {
132 # Prime match after "struct/union {" declaration
133 if (!scalar($declaration =~ m/(?:struct|union)\s+\w*\s*\{/msg)) {
134 die "Declaration '$declaration' does not begin with struct/union [NAME] {\n";
137 #@TRACE@("public fields in struct/union: $declaration");
139 # Treat lines in sequence, allowing singly nested anonymous structs
141 while ($declaration =~ m/\s*([^{;]+(\{[^\}]*\}[^{;]+)?);/msg) {
144 last if $line =~ /^\s*\}\s*\w*\s*$/;
146 # FIXME: Just ignore nested structs and unions for now
147 next if $line =~ /{/;
149 # ignore preprocessor directives
150 while ($line =~ /^#.*?\n\s*(.*)/msg) {
154 last if $line =~ /^\s*\}\s*\w*\s*$/;
156 # Try to match structure members which are functions
158 (const\s
+|G_CONST_RETURN\s
+|unsigned\s
+|signed\s
+|long\s
+|short\s
+)*(struct\s
+|enum\s
+)?
# mod1
160 (\
**(?
:\s
*restrict
)?
)\s
* # ptr1
164 \
(\s
*\
*\s
*(\w
+)\s
*\
)\s
* # name
165 \
(([^)]*)\
)\s
* # func_params
168 my $mod1 = defined($1) ?
$1 : "";
169 if (defined($2)) { $mod1 .= $2; }
172 my $mod2 = defined($5) ?
$5 : "";
174 my $mod3 = defined($7) ?
$7 : "";
176 my $func_params = $9;
177 my $ptype = defined $typefunc ?
$typefunc->($type, "<type>$type</type>") : $type;
178 my $pname = defined $namefunc ?
$namefunc->($name) : $name;
182 if ($output_function_params) {
183 push @result, "$mod1$ptype$ptr1$mod2$ptr2$mod3 (*$pname) ($func_params)";
185 push @result, "$pname ()";
189 # Try to match normal struct fields of comma-separated variables/
190 } elsif ($line =~ m
/^
191 ((?
:const\s
+|volatile\s
+|unsigned\s
+|signed\s
+|short\s
+|long\s
+)?
)(struct\s
+|enum\s
+)?
# mod1
193 (\
** \s
* const\s
+)?
# mod2
197 my $mod1 = defined($1) ?
$1 : "";
198 if (defined($2)) { $mod1 .= $2; }
200 my $ptype = defined $typefunc ?
$typefunc->($type, "<type>$type</type>") : $type;
201 my $mod2 = defined($4) ?
" " . $4 : "";
204 #@TRACE@("'$mod1' '$type' '$mod2' '$list'");
206 $mod1 =~ s/ / /g;
207 $mod2 =~ s/ / /g;
209 my @names = split /,/, $list;
211 # Each variable can have any number of '*' before the
212 # identifier, and be followed by any number of pairs of
213 # brackets or a bit field specifier.
214 # e.g. *foo, ***bar, *baz[12][23], foo : 25.
215 if ($n =~ m/^\s* (\**(?:\s*restrict\b)?) \s* (\w+) \s* (?: ((?:\[[^\]]*\]\s*)+) | (:\s*\d+)?) \s* $/x) {
218 my $array = defined($3) ?
$3 : "";
219 my $bits = defined($4) ?
" $4" : "";
221 if ($ptrs && $ptrs !~ m/\*$/) { $ptrs .= " "; }
222 $array =~ s/ / /g;
223 $bits =~ s/ / /g;
226 if (defined $namefunc) {
227 $name = $namefunc->($name);
229 push @result, "$mod1$ptype$mod2 $ptrs$name$array$bits;";
231 #@TRACE@("Matched line: $mod1$ptype$mod2 $ptrs$name$array$bits");
233 print "WARNING: Couldn't parse struct field: $n\n";
238 print "WARNING: Cannot parse structure field: \"$line\"\n";
246 #############################################################################
247 # Function : ParseEnumDeclaration
248 # Description : This function takes a enumeration declaration and
249 # breaks it into individual enum member declarations.
250 # Arguments : $declaration - the declaration to parse
251 #############################################################################
253 sub ParseEnumDeclaration
{
254 my ($declaration, $is_object) = @_;
256 # For forward enum declarations just return an empty array.
257 if ($declaration =~ m/enum\s+\S+\s*;/msg) {
261 # Remove private symbols
262 # Assume end of declaration if line begins with '}'
263 $declaration =~ s!\n?[ \t]*/\*\s*<\s*(private|protected)\s*>\s*\*/.*?(?:/\*\s*<\s*public\s*>\s*\*/|(?=^\}))!!msgx;
265 # Remove all other comments
266 $declaration =~ s@
\n\s
*/\*([^*]+|\*(?!/))*\
*/\s
*\n@
\n@msg;
267 $declaration =~ s@
/\*([^*]+|\*(?!/))*\
*/@
@g;
268 $declaration =~ s@
\n\s
*//.*?
\n@
\n@msg;
269 $declaration =~ s@
//.*@
@g;
273 if ($declaration =~ /^\s*$/) {
277 # Remove parenthesized expressions (in macros like GTK_BLAH = BLAH(1,3))
278 # to avoid getting confused by commas they might contain. This
279 # doesn't handle nested parentheses correctly.
281 $declaration =~ s/\([^)\n]+\)//g;
283 # Remove apostrophed characters (e.g. '}' or ',') values to avoid getting
284 # confused with end of enumeration.
285 # See https://bugzilla.gnome.org/show_bug.cgi?id=741305
287 $declaration =~ s/\'.\'//g;
289 # Remove comma from comma - possible whitespace - closing brace sequence
290 # since it is legal in GNU C and C99 to have a trailing comma but doesn't
291 # result in an actual enum member
293 $declaration =~ s/,(\s*})/$1/g;
295 # Prime match after "typedef enum {" declaration
296 if (!scalar($declaration =~ m/(typedef\s+)?enum\s*(\S+\s*)?\{/msg)) {
297 die "Enum declaration '$declaration' does not begin with 'typedef enum {' or 'enum XXX {'\n";
300 #@TRACE@("public fields in enum: $declaration");
302 # Treat lines in sequence.
303 while ($declaration =~ m/\s*([^,\}]+)([,\}])/msg) {
307 # ignore preprocessor directives
308 while ($line =~ /^#.*?\n\s*(.*)/msg) {
312 if ($line =~ m/^(\w+)\s*(=.*)?$/msg) {
315 # Special case for GIOCondition, where the values are specified by
316 # macros which expand to include the equal sign like '=1'.
317 } elsif ($line =~ m/^(\w+)\s*GLIB_SYSDEF_POLL/msg) {
320 # Special case include of <gdk/gdkcursors.h>, just ignore it
321 } elsif ($line =~ m/^#include/) {
324 # Special case for #ifdef/#else/#endif, just ignore it
325 } elsif ($line =~ m/^#(?:if|else|endif)/) {
329 warn "Cannot parse enumeration member \"$line\"";
332 last if $terminator eq '}';
339 #############################################################################
340 # Function : ParseFunctionDeclaration
341 # Description : This function takes a function declaration and
342 # breaks it into individual parameter declarations.
343 # Arguments : $declaration - the declaration to parse
344 # $typefunc - function reference to apply to type
345 # $namefunc - function reference to apply to name
346 #############################################################################
348 sub ParseFunctionDeclaration
{
349 my ($declaration, $typefunc, $namefunc) = @_;
354 while ($declaration ne "") {
355 #@TRACE@("[$declaration]");
357 if ($declaration =~ s/^[\s,]+//) {
358 # skip whitespace and commas
361 } elsif ($declaration =~ s/^void\s*[,\n]//) {
362 if ($param_num != 0) {
363 # FIXME: whats the problem here?
364 warn "void used as parameter in function $declaration";
366 push @result, "void";
367 my $xref = "<type>void</type>";
368 my $label = defined $namefunc ?
$namefunc->($xref) : $xref;
369 push @result, $label;
371 } elsif ($declaration =~ s/^\s*[_a-zA-Z0-9]*\.\.\.\s*[,\n]//) {
373 my $label = defined $namefunc ?
$namefunc->("...") : "...";
374 push @result, $label;
376 # allow alphanumerics, '_', '[' & ']' in param names
377 # Try to match a standard parameter
379 } elsif ($declaration =~ s/^\s*((?:(?:G_CONST_RETURN|G_GNUC_[A-Z_]+\s+|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|G_GNUC_[A-Z_]+\b)?\s*\*?\s*(?:const\b|restrict\b|G_GNUC_[A-Z_]+\b)?\s*)*)(\w+)?\s*((?:\[\S*\])*)\s*(?:G_GNUC_[A-Z_]+)?\s*[,\n]//) {
380 my $pre = defined($1) ?
$1 : "";
382 my $ptr = defined($3) ?
$3 : "";
383 my $name = defined($4) ?
$4 : "";
384 my $array = defined($5) ?
$5 : "";
390 if ($ptr && $ptr !~ m/\*$/) { $ptr .= " "; }
392 #@TRACE@("$symbol: '$pre' '$type' '$ptr' '$name' '$array'");
394 if (($name eq "") && $pre =~ m/^((un)?signed .*)\s?/ ) {
401 $name = "Param" . ($param_num + 1);
404 #@TRACE@("$symbol: '$pre' '$type' '$ptr' '$name' '$array'");
407 my $xref = defined $typefunc ?
$typefunc->($type, "<type>$type</type>") : $type;
408 my $label = "$pre$xref $ptr$name$array";
409 if (defined $namefunc) {
410 $label = $namefunc->($label)
412 push @result, $label;
414 # Try to match parameters which are functions
415 # $1 $2 $3 $4 $5 $6 $7 $8
416 } elsif ($declaration =~ s/^(const\s+|G_CONST_RETURN\s+|G_GNUC_[A-Z_]+\s+|signed\s+|unsigned\s+)*(struct\s+)?(\w+)\s*(\**)\s*(?:restrict\b)?\s*(const\s+)?\(\s*(\*[\s\*]*)\s*(\w+)\s*\)\s*\(([^)]*)\)\s*[,\n]//) {
417 my $mod1 = defined($1) ?
$1 : "";
418 if (defined($2)) { $mod1 .= $2; }
421 my $mod2 = defined($5) ?
$5 : "";
424 my $func_params = defined($8) ?
$8 : "";
426 #if (!defined($type)) { print "## no type\n"; };
427 #if (!defined($ptr1)) { print "## no ptr1\n"; };
428 #if (!defined($func_ptr)) { print "## no func_ptr\n"; };
429 #if (!defined($name)) { print "## no name\n"; };
431 if ($ptr1 && $ptr1 !~ m/\*$/) { $ptr1 .= " "; }
432 $func_ptr =~ s/\s+//g;
435 my $xref = defined $typefunc ?
$typefunc->($type, "<type>$type</type>") : $type;
436 #@TRACE@("Type: [$mod1][$xref][$ptr1][$mod2] ([$func_ptr][$name]) ($func_params)");
437 my $label = "$mod1$xref$ptr1$mod2 ($func_ptr$name) ($func_params)";
438 if (defined $namefunc) {
439 $label = $namefunc->($label)
441 push @result, $label;
443 warn "Can't parse args for function in \"$declaration\"";
453 #############################################################################
454 # Function : ParseMacroDeclaration
455 # Description : This function takes a macro declaration and
456 # breaks it into individual parameter declarations.
457 # Arguments : $declaration - the declaration to parse
458 # $namefunc - function reference to apply to name
459 #############################################################################
461 sub ParseMacroDeclaration
{
462 my ($declaration, $namefunc) = @_;
466 if ($declaration =~ m/^\s*#\s*define\s+\w+\(([^\)]*)\)/) {
469 $params =~ s/\\\n//g;
470 foreach $param (split (/,/, $params)) {
473 # Allow varargs variations
474 if ($param =~ m/^.*\.\.\.$/) {
477 if ($param =~ m/\S/) {
478 push @result, $param;
479 push @result, defined $namefunc ?
$namefunc->($param) : $param;
488 #############################################################################
489 # Function : LogWarning
490 # Description : Log a warning in gcc style format
491 # Arguments : $file - the file the error comes from
492 # $line - line number for the wrong entry
493 # $message - description of the issue
494 #############################################################################
497 my ($file, $line, $message) = @_;
499 $file="unknown" if !defined($file);
500 $line="0" if !defined($line);
502 print "$file:$line: warning: $message\n"
508 if (defined($ENV{"GTKDOC_TRACE"})) {
509 my (undef, $file, $line) = caller;
512 print "$file:$line: trace: $message\n"
517 #############################################################################
518 # Function : CreateValidSGMLID
519 # Description : Creates a valid SGML 'id' from the given string.
520 # According to http://www.w3.org/TR/html4/types.html#type-id
521 # "ID and NAME tokens must begin with a letter ([A-Za-z]) and
522 # may be followed by any number of letters, digits ([0-9]),
523 # hyphens ("-"), underscores ("_"), colons (":"), and
526 # NOTE: When creating SGML IDS, we append ":CAPS" to all
527 # all-caps identifiers to prevent name clashes (SGML ids are
528 # case-insensitive). (It basically never is the case that
529 # mixed-case identifiers would collide.)
530 # Arguments : $id - the string to be converted into a valid SGML id.
531 #############################################################################
533 sub CreateValidSGMLID
{
536 # Special case, '_' would end up as '' so we use 'gettext-macro' instead.
537 if ($id eq "_") { return "gettext-macro"; }
545 # Append ":CAPS" to all all-caps identifiers
546 # FIXME: there are some inconsistencies here, we have sgml.index files
547 # containing e.g. TRUE--CAPS
548 if ($id !~ /[a-z]/ && $id !~ /-CAPS$/) { $id .= ":CAPS" };