SDL: docs: heavy editing to make this happy with latest wikibridge.

From e03ad30a57af56f38e7efeaf28d197a902fed57d Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 23 Apr 2024 14:19:47 -0400
Subject: [PATCH] docs: heavy editing to make this happy with latest
 wikibridge.

The public headers saw lots of cleanups, backporting from SDL3 docs, and
merging with the wiki.

The markdown files in docs/README-*.md were converted to Unix endlines.
---
 build-scripts/wikiheaders.pl |  699 ++++++--
 docs/README-android.md       |  966 +++++------
 docs/README-cmake.md         |  326 ++--
 docs/README-directfb.md      |  246 +--
 docs/README-dynapi.md        |  276 ++--
 docs/README-emscripten.md    |  748 ++++-----
 docs/README-gdk.md           |  352 ++--
 docs/README-gesture.md       |  142 +-
 docs/README-git.md           |   38 +-
 docs/README-hg.md            |    8 +-
 docs/README-ios.md           |  614 +++----
 docs/README-kmsbsd.md        |   54 +-
 docs/README-linux.md         |  192 +--
 docs/README-macos.md         |  570 +++----
 docs/README-n3ds.md          |   56 +-
 docs/README-nacl.md          |  206 +--
 docs/README-ngage.md         |   88 +-
 docs/README-os2.md           |  184 +--
 docs/README-pandora.md       |   34 +-
 docs/README-platforms.md     |   16 +-
 docs/README-porting.md       |  136 +-
 docs/README-ps2.md           |  102 +-
 docs/README-psp.md           |   72 +-
 docs/README-raspberrypi.md   |  360 ++--
 docs/README-riscos.md        |   82 +-
 docs/README-touch.md         |  172 +-
 docs/README-versions.md      |  120 +-
 docs/README-visualc.md       |  228 +--
 docs/README-vita.md          |   66 +-
 docs/README-wince.md         |   20 +-
 docs/README-windows.md       |  116 +-
 docs/README-winrt.md         | 1038 ++++++------
 include/SDL_atomic.h         |    5 +-
 include/SDL_audio.h          |   48 +-
 include/SDL_bits.h           |    6 +
 include/SDL_blendmode.h      |    7 +-
 include/SDL_endian.h         |   39 +
 include/SDL_events.h         |  167 +-
 include/SDL_gamecontroller.h |   27 +-
 include/SDL_guid.h           |   32 +-
 include/SDL_haptic.h         |  624 +++----
 include/SDL_hidapi.h         |    5 +-
 include/SDL_hints.h          | 3002 +++++++++++++++++++---------------
 include/SDL_joystick.h       |   20 +-
 include/SDL_keyboard.h       |    9 +-
 include/SDL_keycode.h        |   16 +-
 include/SDL_log.h            |   15 +-
 include/SDL_main.h           |    6 +-
 include/SDL_messagebox.h     |   10 +-
 include/SDL_metal.h          |    4 +-
 include/SDL_mouse.h          |   10 +-
 include/SDL_mutex.h          |    6 +-
 include/SDL_pixels.h         |   32 +-
 include/SDL_power.h          |    2 +-
 include/SDL_quit.h           |    6 +-
 include/SDL_render.h         |    4 +-
 include/SDL_rwops.h          |   10 +-
 include/SDL_scancode.h       |   12 +-
 include/SDL_sensor.h         |  110 +-
 include/SDL_shape.h          |   18 +-
 include/SDL_stdinc.h         |   42 +-
 include/SDL_surface.h        |  130 +-
 include/SDL_system.h         |   10 +-
 include/SDL_syswm.h          |   13 +-
 include/SDL_test.h           |    2 +-
 include/SDL_test_assert.h    |   18 +-
 include/SDL_test_common.h    |   20 +-
 include/SDL_test_compare.h   |    4 +-
 include/SDL_test_font.h      |   22 +-
 include/SDL_test_fuzzer.h    |   57 +-
 include/SDL_test_harness.h   |   10 +-
 include/SDL_test_images.h    |    4 +-
 include/SDL_test_log.h       |    6 +-
 include/SDL_test_md5.h       |    8 +-
 include/SDL_test_memory.h    |    6 +-
 include/SDL_test_random.h    |    8 +-
 include/SDL_thread.h         |   14 +-
 include/SDL_timer.h          |   18 +-
 include/SDL_version.h        |   51 +-
 include/SDL_video.h          |   98 +-
 80 files changed, 6984 insertions(+), 6136 deletions(-)

diff --git a/build-scripts/wikiheaders.pl b/build-scripts/wikiheaders.pl
index 4bdfd5afa1552..b81025e5674d8 100755
--- a/build-scripts/wikiheaders.pl
+++ b/build-scripts/wikiheaders.pl
@@ -2,6 +2,7 @@
 
 use warnings;
 use strict;
+use File::Path;
 use Text::Wrap;
 
 $Text::Wrap::huge = 'overflow';
@@ -31,6 +32,8 @@
 my $wikiheaderfiletext = 'Defined in %fname%';
 my $manpageheaderfiletext = 'Defined in %fname%';
 my $changeformat = undef;
+my $manpath = undef;
+my $gitrev = undef;
 
 foreach (@ARGV) {
     $warn_about_missing = 1, next if $_ eq '--warn-about-missing';
@@ -44,6 +47,12 @@
     } elsif (/\A--changeformat=(.*)\Z/) {
         $changeformat = $1;
         next;
+    } elsif (/\A--manpath=(.*)\Z/) {
+        $manpath = $1;
+        next;
+    } elsif (/\A--rev=(.*)\Z/) {
+        $gitrev = $1;
+        next;
     }
     $srcpath = $_, next if not defined $srcpath;
     $wikipath = $_, next if not defined $wikipath;
@@ -169,6 +178,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";
     }
@@ -308,7 +319,9 @@ sub wikify {
 
     #print("WIKIFY WHOLE:\n\n$str\n\n\n");
 
-    while ($str =~ s/\A(.*?)\`\`\`(c\+\+|c)(.*?)\`\`\`//ms) {
+    # !!! FIXME: this shouldn't check language but rather if there are
+    # !!! FIXME: chars immediately after "```" to a newline.
+    while ($str =~ s/\A(.*?)\`\`\`(c\+\+|c|)(.*?)\`\`\`//ms) {
         $retval .= wikify_chunk($wikitype, $1, $2, $3);
     }
     $retval .= wikify_chunk($wikitype, $str, undef, undef);
@@ -381,8 +394,8 @@ sub dewikify_chunk {
             # <code></code> is also popular.  :/
             $str =~ s/\s*\<code>(.*?)<\/code>\s*/\n.BR $1\n/gms;
 
-            # bold+italic
-            $str =~ s/\s*'''''(.*?)'''''\s*/\n.BI $1\n/gms;
+            # bold+italic (this looks bad, just make it bold).
+            $str =~ s/\s*'''''(.*?)'''''\s*/\n.B $1\n/gms;
 
             # bold
             $str =~ s/\s*'''(.*?)'''\s*/\n.B $1\n/gms;
@@ -404,8 +417,8 @@ sub dewikify_chunk {
             # <code></code> is also popular.  :/
             $str =~ s/\s*\`(.*?)\`\s*/\n.BR $1\n/gms;
 
-            # bold+italic
-            $str =~ s/\s*\*\*\*(.*?)\*\*\*\s*/\n.BI $1\n/gms;
+            # bold+italic (this looks bad, just make it bold).
+            $str =~ s/\s*\*\*\*(.*?)\*\*\*\s*/\n.B $1\n/gms;
 
             # bold
             $str =~ s/\s*\*\*(.*?)\*\*\s*/\n.B $1\n/gms;
@@ -417,7 +430,7 @@ sub dewikify_chunk {
             $str =~ s/^\- /\n\\\(bu /gm;
 
         } else {
-            die("Unexpected wikitype when converting to manpages\n");   # !!! FIXME: need to handle Markdown wiki pages.
+            die("Unexpected wikitype when converting to manpages");   # !!! FIXME: need to handle Markdown wiki pages.
         }
 
         if (defined $code) {
@@ -431,7 +444,7 @@ sub dewikify_chunk {
             $str .= ".EX\n$code\n.EE\n.PP\n";
         }
     } else {
-        die("Unexpected dewikify_mode\n");
+        die("Unexpected dewikify_mode");
     }
 
     #print("\n\nDEWIKIFY CHUNK DONE:\n\n$str\n\n\n");
@@ -478,13 +491,17 @@ sub filecopy {
 }
 
 sub usage {
-    die("USAGE: $0 <source code git clone path> <wiki git clone path> [--copy-to-headers|--copy-to-wiki|--copy-to-manpages] [--warn-about-missing]\n\n");
+    die("USAGE: $0 <source code git clone path> <wiki git clone path> [--copy-to-headers|--copy-to-wiki|--copy-to-manpages] [--warn-about-missing] [--manpath=<man path>]\n\n");
 }
 
 usage() if not defined $srcpath;
 usage() if not defined $wikipath;
 #usage() if $copy_direction == 0;
 
+if (not defined $manpath) {
+    $manpath = "$srcpath/man";
+}
+
 my @standard_wiki_sections = (
     'Draft',
     '[Brief]',
@@ -492,12 +509,15 @@ sub usage {
     '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
@@ -510,11 +530,53 @@ sub usage {
 
 
 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 %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'
+
+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 '';
@@ -532,14 +594,38 @@ sub usage {
     open(FH, '<', "$incpath/$dent") or die("Can't open '$incpath/$dent': $!\n");
 
     my @contents = ();
+    my $ignoring_lines = 0;
 
     while (<FH>) {
         chomp;
+        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 ($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*extern\s+(SDL_DEPRECATED\s+|)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 = '';
@@ -575,7 +661,19 @@ sub usage {
             $decl = <FH>;
             $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+|)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, $_;
@@ -586,76 +684,212 @@ sub usage {
         }
 
         my @decllines = ( $decl );
+        my $sym = '';
 
-        if (not $decl =~ /\)\s*;/) {
-            while (<FH>) {
-                chomp;
-                push @decllines, $_;
-                s/\A\s+//;
-                s/\s+\Z//;
-                $decl .= " $_";
-                last if /\)\s*;/;
+        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;
+                        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;
+                        push @decllines, $_;
+                        s/\A\s+//;
+                        s/\s+\Z//;
+                        $decl .= " $_";
+                        last if /\)\s*;/;
+                    }
+                }
+                $decl =~ s/\s+\);\Z/);/;
             }
-        }
 
-        $decl =~ s/\s+\);\Z/);/;
-        $decl =~ s/\s+\Z//;
-        #print("DECL: [$decl]\n");
+            $decl =~ s/\s+\Z//;
 
-        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, $_;
+            if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)DECLSPEC\s+(const\s+|)(unsigned\s+|)(.*?)\s*(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) {
+                $sym = $6;
+                #$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;
             }
-            foreach (@decllines) {
-                push @contents, $_;
+
+            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+|)DECLSPEC\s+(.*?)\s+(\*?)SDLCALL\s+/$2$3 /;
+                    } 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";
+                }
+            }
+        } 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;
             }
-            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 /;
+            while ($decl =~ /\\\Z/) {
+                my $l = <FH>;
+                last if not $l;
+                chomp($l);
+                push @decllines, $l;
+                #$l =~ s/\A\s+//;
+                $l =~ s/\s+\Z//;
+                $decl .= "\n$l";
+            }
+        } 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;
+            }
+
+            # 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;
+                        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;
+                #my $datatype;
+                if ($tdstr =~ /\A(.*?)\s*\((.*?)\s*\*\s*(.*?)\)\s*\((.*?)\)\s*\Z/) {  # 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";
+        } else {
+            die("Unexpected symtype $symtype");
         }
 
-        #print("$fn:\n$str\n\n");
+        #print("DECL: [$decl]\n");
+
+        #print("$sym:\n$str\n\n");
 
         # There might be multiple declarations of a function due to #ifdefs,
         #  and only one of them will have documentation. If we hit an
         #  undocumented one before, delete the placeholder line we left for
         #  it so it doesn't accumulate a new blank line on each run.
-        my $skipfn = 0;
-        if (defined $headerfuncshasdoxygen{$fn}) {
-            if ($headerfuncshasdoxygen{$fn} == 0) {  # An undocumented declaration already exists, nuke its placeholder line.
-                delete $contents[$headerfuncschunk{$fn}];  # delete DOES NOT RENUMBER existing elements!
+        my $skipsym = 0;
+        if (defined $headersymshasdoxygen{$sym}) {
+            if ($headersymshasdoxygen{$sym} == 0) {  # An undocumented declaration already exists, nuke its placeholder line.
+                delete $contents[$headersymschunk{$sym}];  # delete DOES NOT RENUMBER existing elements!
             } else {  # documented function already existed?
-                $skipfn = 1;  # don't add this copy to the list of functions.
+                $skipsym = 1;  # don't add this copy to the list of functions.
                 if ($has_doxygen) {
-                    print STDERR "WARNING: Function '$fn' appears to be documented in multiple locations. Only keeping the first one we saw!\n";
+                    print STDERR "WARNING: Symbol '$sym' appears to be documented in multiple locations. Only keeping the first one we saw!\n";
                 }
                 push @contents, join("\n", @decllines);  # just put the existing declation in as-is.
             }
         }
 
-        if (!$skipfn) {
-            $headerfuncs{$fn} = $str;
-            $headerdecls{$fn} = $decl;
-            $headerfuncslocation{$fn} = $dent;
-            $headerfuncschunk{$fn} = scalar(@contents);
-            $headerfuncshasdoxygen{$fn} = $has_doxygen;
+        if (!$skipsym) {
+            $headersyms{$sym} = $str;
+            $headerdecls{$sym} = $decl;
+            $headersymslocation{$sym} = $dent;
+            $headersymschunk{$sym} = scalar(@contents);
+            $headersymshasdoxygen{$sym} = $has_doxygen;
+            $headersymstype{$sym} = $symtype;
             push @contents, join("\n", @templines);
             push @contents, join("\n", @decllines);
         }
@@ -667,13 +901,6 @@ sub usage {
 }
 closedir(DH);
 
-
-# !!! FIXME: we need to parse enums and typedefs and structs and defines and and and and and...
-# !!! FIXME:  (but functions are good enough for now.)
-
-my %wikitypes = ();  # contains string of wiki page extension, like $wikitypes{"SDL_OpenAudio"} == 'mediawiki'
-my %wikifuncs = ();  # contains references to hash of strings, each string being the full contents of a section of a wiki page, like $wikifuncs{"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'
 opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n");
 while (my $d = readdir(DH)) {
     my $dent = $d;
@@ -684,14 +911,14 @@ sub usage {
         next;  # only dealing with wiki pages.
     }
 
-    my $fn = $dent;
-    $fn =~ s/\..*\Z//;
+    my $sym = $dent;
+    $sym =~ s/\..*\Z//;
 
     # Ignore FrontPage.
-    next if $fn eq 'FrontPage';
+    next if $sym eq 'FrontPage';
 
     # Ignore "Category*" pages.
-    next if ($fn =~ /\ACategory/);
+    next if ($sym =~ /\ACategory/);
 
     open(FH, '<', "$wikipath/$dent") or die("Can't open '$wikipath/$dent': $!\n");
 
@@ -714,13 +941,13 @@ sub usage {
                 next;
             } elsif (/\A\= (.*?) \=\Z/) {
                 $firstline = 0;
-                $current_section = ($1 eq $fn) ? '[Brief]' : $1;
+                $current_section = ($1 eq $sym) ? '[Brief]' : $1;
                 die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
                 push @section_order, $current_section;
                 $sections{$current_section} = '';
             } elsif (/\A\=\= (.*?) \=\=\Z/) {
                 $firstline = 0;
-                $current_section = ($1 eq $fn) ? '[Brief]' : $1;
+                $current_section = ($1 eq $sym) ? '[Brief]' : $1;
                 die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
                 push @section_order, $current_section;
                 $sections{$current_section} = '';
@@ -739,7 +966,7 @@ sub usage {
                 next;
             } elsif (/\A\#+ (.*?)\Z/) {
                 $firstline = 0;
-                $current_section = ($1 eq $fn) ? '[Brief]' : $1;
+                $current_section = ($1 eq $sym) ? '[Brief]' : $1;
                 die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
                 push @section_order, $current_section;
                 $sections{$current_section} = '';
@@ -753,7 +980,7 @@ sub usage {
                 next;
             }
         } else {
-            die("Unexpected wiki file type. Fixme!\n");
+            die("Unexpected wiki file type. Fixme!");
         }
 
         if ($firstline) {
@@ -771,33 +998,53 @@ sub usage {
         $sections{$_} .= "\n";
     }
 
+    # older section name we used, migrate over from it.
+    if (defined $sections{'Related Functions'}) {
+        if (not defined $sections{'See Also'}) {
+            $sections{'See Also'} = $sections{'Related Functions'};
+        }
+        delete $sections{'Related Functions'};
+    }
+
     if (0) {
         foreach (@section_order) {
-            print("$fn SECTION '$_':\n");
+            print("$sym SECTION '$_':\n");
             print($sections{$_});
             print("\n\n");
         }
     }
 
-    $wikitypes{$fn} = $type;
-    $wikifuncs{$fn} = \%sections;
-    $wikisectionorder{$fn} = \@section_order;
+    $wikitypes{$sym} = $type;
+    $wikisyms{$sym} = \%sections;
+    $wikisectionorder{$sym} = \@section_order;
 }
 closedir(DH);
 
+delete $wikisyms{"Undocumented"};
+
+{
+    my $path = "$wikipath/Undocumented.md";
+    open(my $fh, '>', $path) or die("Can't open '$path': $!\n");
+
+    print $fh "# Undocumented\n\n";
+    print_undocumented_section($fh, 'Functions', 1);
+    #print_undocumented_section($fh, 'Macros', 2);
+
+    close($fh);
+}
 
 if ($warn_about_missing) {
-    foreach (keys %wikifuncs) {
-        my $fn = $_;
-        if (not defined $headerfuncs{$fn}) {
-            print("WARNING: $fn defined in the wiki but not the headers!\n");
+    foreach (keys %wikisyms) {
+        my $sym = $_;
+        if (not defined $headersyms{$sym}) {
+            print("WARNING: $sym defined in the wiki but not the headers!\n");
         }
     }
 
-    foreach (keys %headerfuncs) {
-        my $fn = $_;
-        if (not defined $wikifuncs{$fn}) {
-            print("WARNING: $fn defined in the headers but not the wiki!\n");
+    foreach (keys %headersyms) {
+        my $sym = $_;
+        if (not defined $wikisyms{$sym}) {
+            print("WARNING: $sym defined in the headers but not the wiki!\n");
         }
     }
 }
@@ -808,23 +1055,42 @@ sub usage {
     $dewikify_mode = 'md';
     $wordwrap_mode = 'md';   # the headers use Markdown format.
 
-    foreach (keys %headerfuncs) {
-        my $fn = $_;
-        next if not defined $wikifuncs{$fn};  # don't have a page for that function, skip it.
-        my $wikitype = $wikitypes{$fn};
-        my $sectionsref = $wikifuncs{$fn};
+    foreach (keys %headersyms) {
+        my $sym = $_;
+        next if not defined $wikisyms{$sym};  # don't have a page for that function, skip it.
+        my $symtype = $headersymstype{$sym};
+        my $wikitype = $wikitypes{$sym};
+        my $sectionsref = $wikisyms{$sym};
         my $remarks = $sectionsref->{'Remarks'};
-        my $params = $sectionsref->{'Function Parameters'};
         my $returns = $sectionsref->{'Return Value'};
         my $threadsafety = $sectionsref->{'Thread Safety'};
         my $version = $sectionsref->{'Version'};
-        my $related = $sectionsref->{'Related Functions'};
+        my $related = $sectionsref->{'See Also'};
         my 

(Patch may be truncated, please check the link at the top of this post.)