From 39a4fb54f87b6b6957a4a609a8097e61430e69c1 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 14 May 2024 11:37:52 -0400
Subject: [PATCH] wikiheaders: Updated script from latest in SDL3.
---
build-scripts/wikiheaders.pl | 1344 +++++++++++++++++++++++++++-----
include/SDL3_mixer/SDL_mixer.h | 8 +-
2 files changed, 1134 insertions(+), 218 deletions(-)
diff --git a/build-scripts/wikiheaders.pl b/build-scripts/wikiheaders.pl
index c2bb8edb..185701b4 100755
--- a/build-scripts/wikiheaders.pl
+++ b/build-scripts/wikiheaders.pl
@@ -29,6 +29,9 @@
my $copy_direction = 0;
my $optionsfname = undef;
my $wikipreamble = undef;
+my $wikiheaderfiletext = 'Defined in %fname%';
+my $manpageheaderfiletext = 'Defined in %fname%';
+my $headercategoryeval = undef;
my $changeformat = undef;
my $manpath = undef;
my $gitrev = undef;
@@ -39,6 +42,8 @@
$copy_direction = 1, next if $_ eq '--copy-to-header';
$copy_direction = -1, next if $_ eq '--copy-to-wiki';
$copy_direction = -2, next if $_ eq '--copy-to-manpages';
+ $copy_direction = -3, next if $_ eq '--report-coverage-gaps';
+ $copy_direction = -4, next if $_ eq '--copy-to-latex';
if (/\A--options=(.*)\Z/) {
$optionsfname = $1;
next;
@@ -66,6 +71,8 @@
if (defined $optionsfname) {
open OPTIONS, '<', $optionsfname or die("Failed to open options file '$optionsfname': $!\n");
while (<OPTIONS>) {
+ next if /\A\s*\#/; # Skip lines that start with (optional whitespace, then) '#' as comments.
+
chomp;
if (/\A(.*?)\=(.*)\Z/) {
my $key = $1;
@@ -93,11 +100,20 @@
$wikiurl = $val, next if $key eq 'wikiurl';
$bugreporturl = $val, next if $key eq 'bugreporturl';
$wikipreamble = $val, next if $key eq 'wikipreamble';
+ $wikiheaderfiletext = $val, next if $key eq 'wikiheaderfiletext';
+ $manpageheaderfiletext = $val, next if $key eq 'manpageheaderfiletext';
+ $headercategoryeval = $val, next if $key eq 'headercategoryeval';
}
}
close(OPTIONS);
}
+sub escLaTeX {
+ my $str = shift;
+ $str =~ s/([_\#\&\^])/\\$1/g;
+ return $str;
+}
+
my $wordwrap_mode = 'mediawiki';
sub wordwrap_atom { # don't call this directly.
my $str = shift;
@@ -174,6 +190,8 @@ sub wordwrap_one_paragraph { # don't call this directly.
if ($item ne '') {
$retval .= wordwrap_with_bullet_indent($bullet, $item);
}
+ } elsif ($p =~ /\A\s*\|.*\|\s*\n/) { # Markdown table
+ $retval = "$p\n"; # don't wrap it (!!! FIXME: but maybe parse by lines until we run out of table...)
} else {
$retval = wordwrap_atom($p) . "\n";
}
@@ -297,7 +315,7 @@ sub wikify_chunk {
$str = $codedstr . $str;
if (defined $code) {
- $str .= "```$codelang$code```";
+ $str .= "```$codelang\n$code\n```\n";
}
}
@@ -313,7 +331,7 @@ sub wikify {
#print("WIKIFY WHOLE:\n\n$str\n\n\n");
- while ($str =~ s/\A(.*?)\`\`\`(c\+\+|c)(.*?)\`\`\`//ms) {
+ while ($str =~ s/\A(.*?)\`\`\`(.*?)\n(.*?)\n\`\`\`(\n|\Z)//ms) {
$retval .= wikify_chunk($wikitype, $1, $2, $3);
}
$retval .= wikify_chunk($wikitype, $str, undef, undef);
@@ -370,7 +388,7 @@ sub dewikify_chunk {
}
if (defined $code) {
- $str .= "```$codelang$code```";
+ $str .= "\n```$codelang\n$code\n```\n";
}
} elsif ($dewikify_mode eq 'manpage') {
$str =~ s/\./\\[char46]/gms; # make sure these can't become control codes.
@@ -420,9 +438,6 @@ sub dewikify_chunk {
# bullets
$str =~ s/^\- /\n\\\(bu /gm;
-
- } else {
- die("Unexpected wikitype when converting to manpages\n"); # !!! FIXME: need to handle Markdown wiki pages.
}
if (defined $code) {
@@ -435,8 +450,80 @@ sub dewikify_chunk {
}
$str .= ".EX\n$code\n.EE\n.PP\n";
}
+ } elsif ($dewikify_mode eq 'LaTeX') {
+ if ($wikitype eq 'mediawiki') {
+ # Dump obvious wikilinks.
+ if (defined $apiprefixregex) {
+ $str =~ s/\s*\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]/$1/gms;
+ }
+
+ # links
+ $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\\href{$1}{$2}/g;
+
+ # <code></code> is also popular. :/
+ $str =~ s/\s*\<code>(.*?)<\/code>/ \\texttt{$1}/gms;
+
+ # bold+italic
+ $str =~ s/\s*'''''(.*?)'''''/ \\textbf{\\textit{$1}}/gms;
+
+ # bold
+ $str =~ s/\s*'''(.*?)'''/ \\textbf{$1}/gms;
+
+ # italic
+ $str =~ s/\s*''(.*?)''/ \\textit{$1}/gms;
+
+ # bullets
+ $str =~ s/^\*\s+/ \\item /gm;
+ } elsif ($wikitype eq 'md') {
+ # Dump obvious wikilinks.
+ if (defined $apiprefixregex) {
+ $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/$1/gms;
+ }
+
+ # links
+ $str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\\href{$2}{$1}/g;
+
+ # <code></code> is also popular. :/
+ $str =~ s/\s*\`(.*?)\`/ \\texttt{$1}/gms;
+
+ # bold+italic
+ $str =~ s/\s*\*\*\*(.*?)\*\*\*/ \\textbf{\\textit{$1}}/gms;
+
+ # bold
+ $str =~ s/\s*\*\*(.*?)\*\*/ \\textbf{$1}/gms;
+
+ # italic
+ $str =~ s/\s*\*(.*?)\*/ \\textit{$1}/gms;
+
+ # bullets
+ $str =~ s/^\-\s+/ \\item /gm;
+ }
+
+ # Wrap bullet lists in itemize blocks...
+ $str =~ s/^(\s*\\item .*?)(\n\n|\Z)/\n\\begin{itemize}\n$1$2\n\\end{itemize}\n\n/gms;
+
+ $str = escLaTeX($str);
+
+ if (defined $code) {
+ $code =~ s/\A\n+//gms;
+ $code =~ s/\n+\Z//gms;
+
+ if (($codelang eq '') || ($codelang eq 'output')) {
+ $str .= "\\begin{verbatim}\n$code\n\\end{verbatim}\n";
+ } else {
+ if ($codelang eq 'c') {
+ $codelang = 'C';
+ } elsif ($codelang eq 'c++') {
+ $codelang = 'C++';
+ } else {
+ die("Unexpected codelang '$codelang'");
+ }
+ $str .= "\n\\lstset{language=$codelang}\n";
+ $str .= "\\begin{lstlisting}\n$code\n\\end{lstlisting}\n";
+ }
+ }
} else {
- die("Unexpected dewikify_mode\n");
+ die("Unexpected dewikify_mode");
}
#print("\n\nDEWIKIFY CHUNK DONE:\n\n$str\n\n\n");
@@ -455,8 +542,14 @@ sub dewikify {
$str =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms;
my $retval = '';
- while ($str =~ s/\A(.*?)<syntaxhighlight lang='?(.*?)'?>(.*?)<\/syntaxhighlight\>//ms) {
- $retval .= dewikify_chunk($wikitype, $1, $2, $3);
+ if ($wikitype eq 'mediawiki') {
+ while ($str =~ s/\A(.*?)<syntaxhighlight lang='?(.*?)'?>(.*?)<\/syntaxhighlight\>//ms) {
+ $retval .= dewikify_chunk($wikitype, $1, $2, $3);
+ }
+ } elsif ($wikitype eq 'md') {
+ while ($str =~ s/\A(.*?)\n```(.*?)\n(.*?)\n```\n//ms) {
+ $retval .= dewikify_chunk($wikitype, $1, $2, $3);
+ }
}
$retval .= dewikify_chunk($wikitype, $str, undef, undef);
@@ -498,30 +591,93 @@ sub usage {
'Draft',
'[Brief]',
'Deprecated',
+ 'Header File',
'Syntax',
'Function Parameters',
+ 'Macro Parameters',
+ 'Fields',
+ 'Values',
'Return Value',
'Remarks',
'Thread Safety',
'Version',
'Code Examples',
- 'Related Functions'
+ 'See Also'
);
# Sections that only ever exist in the wiki and shouldn't be deleted when
# not found in the headers.
my %only_wiki_sections = ( # The ones don't mean anything, I just need to check for key existence.
'Draft', 1,
- 'Code Examples', 1
+ 'Code Examples', 1,
+ 'Header File', 1
);
my %headers = (); # $headers{"SDL_audio.h"} -> reference to an array of all lines of text in SDL_audio.h.
-my %headerfuncs = (); # $headerfuncs{"SDL_OpenAudio"} -> string of header documentation for SDL_OpenAudio, with comment '*' bits stripped from the start. Newlines embedded!
+my %headersyms = (); # $headersyms{"SDL_OpenAudio"} -> string of header documentation for SDL_OpenAudio, with comment '*' bits stripped from the start. Newlines embedded!
my %headerdecls = ();
-my %headerfuncslocation = (); # $headerfuncslocation{"SDL_OpenAudio"} -> name of header holding SDL_OpenAudio define ("SDL_audio.h" in this case).
-my %headerfuncschunk = (); # $headerfuncschunk{"SDL_OpenAudio"} -> offset in array in %headers that should be replaced for this function.
-my %headerfuncshasdoxygen = (); # $headerfuncschunk{"SDL_OpenAudio"} -> 1 if there was no existing doxygen for this function.
+my %headersymslocation = (); # $headersymslocation{"SDL_OpenAudio"} -> name of header holding SDL_OpenAudio define ("SDL_audio.h" in this case).
+my %headersymschunk = (); # $headersymschunk{"SDL_OpenAudio"} -> offset in array in %headers that should be replaced for this symbol.
+my %headersymshasdoxygen = (); # $headersymshasdoxygen{"SDL_OpenAudio"} -> 1 if there was no existing doxygen for this function.
+my %headersymstype = (); # $headersymstype{"SDL_OpenAudio"} -> 1 (function), 2 (macro), 3 (struct), 4 (enum), 5 (other typedef)
+my %headersymscategory = (); # $headersymscategory{"SDL_OpenAudio"} -> 'Audio' ... this is set with a `/* WIKI CATEGEORY: Audio */` comment in the headers that sets it on all symbols until a new comment changes it. So usually, once at the top of the header file.
+
+my %wikitypes = (); # contains string of wiki page extension, like $wikitypes{"SDL_OpenAudio"} == 'mediawiki'
+my %wikisyms = (); # contains references to hash of strings, each string being the full contents of a section of a wiki page, like $wikisyms{"SDL_OpenAudio"}{"Remarks"}.
+my %wikisectionorder = (); # contains references to array, each array item being a key to a wikipage section in the correct order, like $wikisectionorder{"SDL_OpenAudio"}[2] == 'Remarks'
+
+my %referenceonly = (); # $referenceonly{"Y"} -> symbol name that this symbol is bound to. This makes wiki pages that say "See X" where "X" is a typedef and "Y" is a define attached to it. These pages are generated in the wiki only and do not bridge to the headers or manpages.
+
+my @coverage_gap = (); # array of strings that weren't part of documentation, or blank, or basic preprocessor logic. Lets you see what this script is missing!
+
+sub add_coverage_gap {
+ if ($copy_direction == -3) { # --report-coverage-gaps
+ my $text = shift;
+ my $dent = shift;
+ my $lineno = shift;
+ return if $text =~ /\A\s*\Z/; # skip blank lines
+ return if $text =~ /\A\s*\#\s*(if|el|endif|include)/; # skip preprocessor floof.
+ push @coverage_gap, "$dent:$lineno: $text";
+ }
+}
+
+sub print_undocumented_section {
+ my $fh = shift;
+ my $typestr = shift;
+ my $typeval = shift;
+
+ print $fh "## $typestr defined in the headers, but not in the wiki\n\n";
+ my $header_only_sym = 0;
+ foreach (sort keys %headersyms) {
+ my $sym = $_;
+ if ((not defined $wikisyms{$sym}) && ($headersymstype{$sym} == $typeval)) {
+ print $fh "- [$sym]($sym)\n";
+ $header_only_sym = 1;
+ }
+ }
+ if (!$header_only_sym) {
+ print $fh "(none)\n";
+ }
+ print $fh "\n";
+
+ if (0) { # !!! FIXME: this lists things that _shouldn't_ be in the headers, like MigrationGuide, etc, but also we don't know if they're functions, macros, etc at this point (can we parse that from the wiki page, though?)
+ print $fh "## $typestr defined in the wiki, but not in the headers\n\n";
+
+ my $wiki_only_sym = 0;
+ foreach (sort keys %wikisyms) {
+ my $sym = $_;
+ if ((not defined $headersyms{$sym}) && ($headersymstype{$sym} == $typeval)) {
+ print $fh "- [$sym]($sym)\n";
+ $wiki_only_sym = 1;
+ }
+ }
+ if (!$wiki_only_sym) {
+ print $fh "(none)\n";
+ }
+ print $fh "\n";
+ }
+}
my $incpath = "$srcpath";
$incpath .= "/$incsubdir" if $incsubdir ne '';
@@ -538,32 +694,85 @@ sub usage {
next if not $dent =~ /$selectheaderregex/; # just selected headers.
open(FH, '<', "$incpath/$dent") or die("Can't open '$incpath/$dent': $!\n");
- my @contents = ();
+ # You can optionally set a wiki category with Perl code in .wikiheaders-options that gets eval()'d per-header,
+ # and also if you put `/* WIKI CATEGORY: blah */` on a line by itself, it'll change the category for any symbols
+ # below it in the same file. If no category is set, one won't be added for the symbol (beyond the standard CategoryFunction, etc)
+ my $current_wiki_category = undef;
+ if (defined $headercategoryeval) {
+ $_ = $dent;
+ $current_wiki_category = eval($headercategoryeval);
+ if (($current_wiki_category eq '') || ($current_wiki_category eq '-')) {
+ $current_wiki_category = undef;
+ }
+ #print("CATEGORY FOR '$dent' IS " . (defined($current_wiki_category) ? "'$current_wiki_category'" : '(undef)') . "\n");
+ }
+ my @contents = ();
+ my $ignoring_lines = 0;
+ my $header_comment = -1;
+ my $lineno = 0;
while (<FH>) {
chomp;
+ $lineno++;
+ my $symtype = 0; # nothing, yet.
my $decl;
my @templines;
my $str;
my $has_doxygen = 1;
- if (/\A\s*extern\s+(SDL_DEPRECATED\s+|)DECLSPEC/) { # a function declaration without a doxygen comment?
+
+ # Since a lot of macros are just preprocessor logic spam and not all macros are worth documenting anyhow, we only pay attention to them when they have a Doxygen comment attached.
+ # Functions and other things are a different story, though!
+
+ if ($header_comment == -1) {
+ $header_comment = /\A\/\*\s*\Z/ ? 1 : 0;
+ } elsif (($header_comment == 1) && (/\A\*\/\s*\Z/)) {
+ $header_comment = 0;
+ }
+
+ if ($ignoring_lines && /\A\s*\#\s*endif\s*\Z/) {
+ $ignoring_lines = 0;
+ push @contents, $_;
+ next;
+ } elsif ($ignoring_lines) {
+ push @contents, $_;
+ next;
+ } elsif (/\A\s*\#\s*ifndef\s+SDL_WIKI_DOCUMENTATION_SECTION\s*\Z/) {
+ $ignoring_lines = 1;
+ push @contents, $_;
+ next;
+ } elsif (/\A\s*\/\*\s*WIKI CATEGORY:\s*(.*?)\s*\*\/\s*\Z/) {
+ $current_wiki_category = (($1 eq '') || ($1 eq '-')) ? undef : $1;
+ #print("CATEGORY FOR '$dent' CHANGED TO " . (defined($current_wiki_category) ? "'$current_wiki_category'" : '(undef)') . "\n");
+ push @contents, $_;
+ next;
+ } elsif (/\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_)?DECLSPEC/) { # a function declaration without a doxygen comment?
+ $symtype = 1; # function declaration
+ @templines = ();
+ $decl = $_;
+ $str = '';
+ $has_doxygen = 0;
+ } elsif (/\A\s*SDL_FORCE_INLINE/) { # a (forced-inline) function declaration without a doxygen comment?
+ $symtype = 1; # function declaration
@templines = ();
$decl = $_;
$str = '';
$has_doxygen = 0;
} elsif (not /\A\/\*\*\s*\Z/) { # not doxygen comment start?
push @contents, $_;
+ add_coverage_gap($_, $dent, $lineno) if ($header_comment == 0);
next;
} else { # Start of a doxygen comment, parse it out.
@templines = ( $_ );
while (<FH>) {
chomp;
+ $lineno++;
push @templines, $_;
last if /\A\s*\*\/\Z/;
if (s/\A\s*\*\s*\`\`\`/```/) { # this is a hack, but a lot of other code relies on the whitespace being trimmed, but we can't trim it in code blocks...
$str .= "$_\n";
while (<FH>) {
chomp;
+ $lineno++;
push @templines, $_;
s/\A\s*\*\s?//;
if (s/\A\s*\`\`\`/```/) {
@@ -580,89 +789,357 @@ sub usage {
}
$decl = <FH>;
+ $lineno++ if defined $decl;
$decl = '' if not defined $decl;
chomp($decl);
- if (not $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)DECLSPEC/) {
+ if ($decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_)?DECLSPEC/) {
+ $symtype = 1; # function declaration
+ } elsif ($decl =~ /\A\s*SDL_FORCE_INLINE/) {
+ $symtype = 1; # (forced-inline) function declaration
+ } elsif ($decl =~ /\A\s*\#\s*define\s+/) {
+ $symtype = 2; # macro
+ } elsif ($decl =~ /\A\s*(typedef\s+|)(struct|union)/) {
+ $symtype = 3; # struct or union
+ } elsif ($decl =~ /\A\s*(typedef\s+|)enum/) {
+ $symtype = 4; # enum
+ } elsif ($decl =~ /\A\s*typedef\s+.*\Z/) {
+ $symtype = 5; # other typedef
+ } else {
#print "Found doxygen but no function sig:\n$str\n\n";
foreach (@templines) {
push @contents, $_;
+ add_coverage_gap($_, $dent, $lineno);
}
push @contents, $decl;
+ add_coverage_gap($decl, $dent, $lineno);
next;
}
}
my @decllines = ( $decl );
+ my $sym = '';
+
+ if ($symtype == 1) { # a function
+ my $is_forced_inline = ($decl =~ /\A\s*SDL_FORCE_INLINE/);
+
+ if ($is_forced_inline) {
+ if (not $decl =~ /\)\s*(\{.*|)\s*\Z/) {
+ while (<FH>) {
+ chomp;
+ $lineno++;
+ push @decllines, $_;
+ s/\A\s+//;
+ s/\s+\Z//;
+ $decl .= " $_";
+ last if /\)\s*(\{.*|)\s*\Z/;
+ }
+ }
+ $decl =~ s/\s*\)\s*(\{.*|)\s*\Z/);/;
+ } else {
+ if (not $decl =~ /\)\s*;/) {
+ while (<FH>) {
+ chomp;
+ $lineno++;
+ push @decllines, $_;
+ s/\A\s+//;
+ s/\s+\Z//;
+ $decl .= " $_";
+ last if /\)\s*;/;
+ }
+ }
+ $decl =~ s/\s+\);\Z/);/;
+ }
+
+ $decl =~ s/\s+\Z//;
+
+ if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_)?DECLSPEC\s+(const\s+|)(unsigned\s+|)(.*?)\s*(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) {
+ $sym = $7;
+ #$decl =~ s/\A\s*extern\s+DECLSPEC\s+(.*?)\s+SDLCALL/$1/;
+ } elsif ($is_forced_inline && $decl =~ /\A\s*SDL_FORCE_INLINE\s+(SDL_DEPRECATED\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/) {
+ $sym = $6;
+ } else {
+ #print "Found doxygen but no function sig:\n$str\n\n";
+ foreach (@templines) {
+ push @contents, $_;
+ }
+ foreach (@decllines) {
+ push @contents, $_;
+ }
+ next;
+ }
- if (not $decl =~ /\)\s*;/) {
+ if (!$is_forced_inline) { # !!! FIXME: maybe we need to do this for forced-inline stuff too?
+ $decl = ''; # build this with the line breaks, since it looks better for syntax highlighting.
+ foreach (@decllines) {
+ if ($decl eq '') {
+ $decl = $_;
+ $decl =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_)?DECLSPEC\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 /;
+ } else {
+ my $trimmed = $_;
+ # !!! FIXME: trim space for SDL_DEPRECATED if it was used, too.
+ $trimmed =~ s/\A\s{24}//; # 24 for shrinking to match the removed "extern DECLSPEC SDLCALL "
+ $decl .= $trimmed;
+ }
+ $decl .= "\n";
+ }
+ }
+
+ # !!! FIXME: code duplication with typedef processing, below.
+ # We assume any `#define`s directly after the function are related to it: probably bitflags for an integer typedef.
+ # We'll also allow some other basic preprocessor lines.
+ # Blank lines are allowed, anything else, even comments, are not.
+ my $blank_lines = 0;
+ my $lastpos = tell(FH);
+ my $lastlineno = $lineno;
+ my $additional_decl = '';
+ my $saw_define = 0;
while (<FH>) {
chomp;
- push @decllines, $_;
- s/\A\s+//;
- s/\s+\Z//;
- $decl .= " $_";
- last if /\)\s*;/;
+
+ $lineno++;
+
+ if (/\A\s*\Z/) {
+ $blank_lines++;
+ } elsif (/\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/) {
+ if (/\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/) {
+ $referenceonly{$1} = $sym;
+ $saw_define = 1;
+ } elsif (!$saw_define) {
+ # if the first non-blank thing isn't a #define, assume we're done.
+ seek(FH, $lastpos, 0); # re-read eaten lines again next time.
+ $lineno = $lastlineno;
+ last;
+ }
+
+ # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
+
+ # At Sam's request, don't list property defines with functions. (See #9440)
+ my $is_property = /\A\s*\#\s*define\s+SDL_PROP_/;
+ if (!$is_property) {
+ if ($blank_lines > 0) {
+ while ($blank_lines > 0) {
+ $additional_decl .= "\n";
+ push @decllines, '';
+ $blank_lines--;
+ }
+ }
+ $additional_decl .= "\n$_";
+ push @decllines, $_;
+ $lastpos = tell(FH);
+ }
+ } else {
+ seek(FH, $lastpos, 0); # re-read eaten lines again next time.
+ $lineno = $lastlineno;
+ last;
+ }
}
- }
+ $decl .= $additional_decl;
- $decl =~ s/\s+\);\Z/);/;
- $decl =~ s/\s+\Z//;
- #print("DECL: [$decl]\n");
+ } elsif ($symtype == 2) { # a macro
+ if ($decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)\s+/) {
+ $sym = $1;
+ #$decl =~ s/\A\s*extern\s+DECLSPEC\s+(.*?)\s+SDLCALL/$1/;
+ } else {
+ #print "Found doxygen but no macro:\n$str\n\n";
+ foreach (@templines) {
+ push @contents, $_;
+ }
+ foreach (@decllines) {
+ push @contents, $_;
+ }
+ next;
+ }
- my $fn = '';
- if ($decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)DECLSPEC\s+(const\s+|)(unsigned\s+|)(.*?)\s*(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) {
- $fn = $6;
- #$decl =~ s/\A\s*extern\s+DECLSPEC\s+(.*?)\s+SDLCALL/$1/;
- } else {
- #print "Found doxygen but no function sig:\n$str\n\n";
- foreach (@templines) {
- push @contents, $_;
+ while ($decl =~ /\\\Z/) {
+ my $l = <FH>;
+ last if not $l;
+ $lineno++;
+ chomp($l);
+ push @decllines, $l;
+ #$l =~ s/\A\s+//;
+ $l =~ s/\s+\Z//;
+ $decl .= "\n$l";
}
- foreach (@decllines) {
- push @contents, $_;
+ } elsif (($symtype == 3) || ($symtype == 4)) { # struct or union or enum
+ my $has_definition = 0;
+ if ($decl =~ /\A\s*(typedef\s+|)(struct|union|enum)\s*(.*?)\s*(\n|\{|\;|\Z)/) {
+ my $ctype = $2;
+ my $origsym = $3;
+ my $ending = $4;
+ $sym = $origsym;
+ if ($sym =~ s/\A(.*?)(\s+)(.*?)\Z/$1/) {
+ die("Failed to parse '$origsym' correctly!") if ($sym ne $1); # Thought this was "typedef struct MySym MySym;" ... it was not. :( This is a hack!
+ }
+ if ($sym eq '') {
+ die("\n\n$0 FAILURE!\n" .
+ "There's a 'typedef $ctype' in $incpath/$dent without a name at the top.\n" .
+ "Instead of `typedef $ctype {} x;`, this should be `typedef $ctype x {} x;`.\n" .
+ "This causes problems for wikiheaders.pl and scripting language bindings.\n" .
+ "Please fix it!\n\n");
+ }
+ $has_definition = ($ending ne ';');
+ } else {
+ #print "Found doxygen but no datatype:\n$str\n\n";
+ foreach (@templines) {
+ push @contents, $_;
+ }
+ foreach (@decllines) {
+ push @contents, $_;
+ }
+ next;
}
- next;
- }
- $decl = ''; # build this with the line breaks, since it looks better for syntax highlighting.
- foreach (@decllines) {
- if ($decl eq '') {
- $decl = $_;
- $decl =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)DECLSPEC\s+(.*?)\s+(\*?)SDLCALL\s+/$2$3 /;
+ # This block attempts to find the whole struct/union/enum definition by counting matching brackets. Kind of yucky.
+ if ($has_definition) {
+ my $started = 0;
+ my $brackets = 0;
+ my $pending = $decl;
+
+ $decl = '';
+ while (!$started || ($brackets != 0)) {
+ foreach my $seg (split(/([{}])/, $pending)) {
+ $decl .= $seg;
+ if ($seg eq '{') {
+ $started = 1;
+ $brackets++;
+ } elsif ($seg eq '}') {
+ die("Something is wrong with header $incpath/$dent while parsing $sym; is a bracket missing?\n") if ($brackets <= 0);
+ $brackets--;
+ }
+ }
+
+ if (!$started || ($brackets != 0)) {
+ $pending = <FH>;
+ die("EOF/error reading $incpath/$dent while parsing $sym\n") if not $pending;
+ $lineno++;
+ chomp($pending);
+ push @decllines, $pending;
+ $decl .= "\n";
+ }
+ }
+ # this currently assumes the struct/union/enum ends on the line with the final bracket. I'm not writing a C parser here, fix the header!
+ }
+ } elsif ($symtype == 5) { # other typedef
+ if ($decl =~ /\A\s*typedef\s+(.*)\Z/) {
+ my $tdstr = $1;
+
+ if (not $decl =~ /;/) {
+ while (<FH>) {
+ chomp;
+ $lineno++;
+ push @decllines, $_;
+ s/\A\s+//;
+ s/\s+\Z//;
+ $decl .= " $_";
+ last if /;/;
+ }
+ }
+ $decl =~ s/\s+(\))?;\Z/$1;/;
+
+ $tdstr =~ s/;\s*\Z//;
+
+ #my $datatype;
+ if ($tdstr =~ /\A(.*?)\s*\((.*?)\s*\*\s*(.*?)\)\s*\((.*?)(\))?/) { # a function pointer type
+ $sym = $3;
+ #$datatype = "$1 ($2 *$sym)($4)";
+ } elsif ($tdstr =~ /\A(.*[\s\*]+)(.*?)\s*\Z/) {
+ $sym = $2;
+ #$datatype = $1;
+ } else {
+ die("Failed to parse typedef '$tdstr' in $incpath/$dent!\n"); # I'm hitting a C grammar nail with a regexp hammer here, y'all.
+ }
+
+ $sym =~ s/\A\s+//;
+ $sym =~ s/\s+\Z//;
+ #$datatype =~ s/\A\s+//;
+ #$datatype =~ s/\s+\Z//;
} else {
- my $trimmed = $_;
- # !!! FIXME: trim space for SDL_DEPRECATED if it was used, too.
- $trimmed =~ s/\A\s{24}//; # 24 for shrinking to match the removed "extern DECLSPEC SDLCALL "
- $decl .= $trimmed;
+ #print "Found doxygen but no datatype:\n$str\n\n";
+ foreach (@templines) {
+ push @contents, $_;
+ }
+ foreach (@decllines) {
+ push @contents, $_;
+ }
+ next;
}
- $decl .= "\n";
+
+ # We assume any `#define`s directly after the typedef are related to it: probably bitflags for an integer typedef.
+ # We'll also allow some other basic preprocessor lines.
+ # Blank lines are allowed, anything else, even comments, are not.
+ my $blank_lines = 0;
+ my $lastpos = tell(FH);
+ my $lastlineno = $lineno;
+ my $additional_decl = '';
+ my $saw_define = 0;
+ while (<FH>) {
+ chomp;
+
+ $lineno++;
+
+ if (/\A\s*\Z/) {
+ $blank_lines++;
+ } elsif (/\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/) {
+ if (/\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/) {
+ $referenceonly{$1} = $sym;
+ $saw_define = 1;
+ } elsif (!$saw_define) {
+ # if the first non-blank thing isn't a #define, assume we're done.
+ seek(FH, $lastpos, 0); # re-read eaten lines again next time.
+ $lineno = $lastlineno;
+ last;
+ }
+ # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
+ if ($blank_lines > 0) {
+ while ($blank_lines > 0) {
+ $additional_decl .= "\n";
+ push @decllines, '';
+ $blank_lines--;
+ }
+ }
+ $additional_decl .= "\n$_";
+ push @decllines, $_;
+ $lastpos = tell(FH);
+ } else {
+
(Patch may be truncated, please check the link at the top of this post.)