2021-03-17 01:46:03 +08:00
#!/usr/bin/perl -w
2024-07-23 10:38:40 +08:00
# Simple DirectMedia Layer
# Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
2021-03-17 01:46:03 +08:00
use warnings ;
use strict ;
2023-03-18 07:25:44 +08:00
use File::Path ;
2021-03-17 01:46:03 +08:00
use Text::Wrap ;
2022-05-25 21:30:29 +08:00
$ Text:: Wrap:: huge = 'overflow' ;
2022-06-16 11:25:36 +08:00
my $ projectfullname = 'Simple Directmedia Layer' ;
my $ projectshortname = 'SDL' ;
my $ wikisubdir = '' ;
my $ incsubdir = 'include' ;
2023-03-01 00:37:46 +08:00
my $ readmesubdir = undef ;
2022-06-16 11:25:36 +08:00
my $ apiprefixregex = undef ;
2024-05-16 05:22:41 +08:00
my $ versionfname = 'include/SDL_version.h' ;
2022-06-16 11:25:36 +08:00
my $ versionmajorregex = '\A\#define\s+SDL_MAJOR_VERSION\s+(\d+)\Z' ;
my $ versionminorregex = '\A\#define\s+SDL_MINOR_VERSION\s+(\d+)\Z' ;
2024-05-14 22:47:13 +08:00
my $ versionmicroregex = '\A\#define\s+SDL_MICRO_VERSION\s+(\d+)\Z' ;
2022-06-16 11:25:36 +08:00
my $ mainincludefname = 'SDL.h' ;
my $ selectheaderregex = '\ASDL.*?\.h\Z' ;
my $ projecturl = 'https://libsdl.org/' ;
my $ wikiurl = 'https://wiki.libsdl.org' ;
my $ bugreporturl = 'https://github.com/libsdl-org/sdlwiki/issues/new' ;
2021-03-17 01:46:03 +08:00
my $ srcpath = undef ;
my $ wikipath = undef ;
2023-03-01 00:37:46 +08:00
my $ wikireadmesubdir = 'README' ;
2021-03-17 01:46:03 +08:00
my $ warn_about_missing = 0 ;
my $ copy_direction = 0 ;
2022-06-16 11:25:36 +08:00
my $ optionsfname = undef ;
2022-06-18 02:39:50 +08:00
my $ wikipreamble = undef ;
2024-04-01 00:56:00 +08:00
my $ wikiheaderfiletext = 'Defined in %fname%' ;
my $ manpageheaderfiletext = 'Defined in %fname%' ;
2024-05-14 22:19:34 +08:00
my $ headercategoryeval = undef ;
2023-02-24 08:53:53 +08:00
my $ changeformat = undef ;
2023-03-18 07:25:44 +08:00
my $ manpath = undef ;
2023-09-05 00:56:00 +08:00
my $ gitrev = undef ;
2021-03-17 01:46:03 +08:00
foreach ( @ ARGV ) {
$ warn_about_missing = 1 , next if $ _ eq '--warn-about-missing' ;
$ copy_direction = 1 , next if $ _ eq '--copy-to-headers' ;
2021-07-15 05:07:41 +08:00
$ copy_direction = 1 , next if $ _ eq '--copy-to-header' ;
2021-03-17 01:46:03 +08:00
$ copy_direction = - 1 , next if $ _ eq '--copy-to-wiki' ;
2022-01-07 04:37:05 +08:00
$ copy_direction = - 2 , next if $ _ eq '--copy-to-manpages' ;
2024-05-02 07:58:21 +08:00
$ copy_direction = - 3 , next if $ _ eq '--report-coverage-gaps' ;
2024-05-05 14:03:44 +08:00
$ copy_direction = - 4 , next if $ _ eq '--copy-to-latex' ;
2022-06-16 11:25:36 +08:00
if ( /\A--options=(.*)\Z/ ) {
$ optionsfname = $ 1 ;
next ;
2023-02-24 08:53:53 +08:00
} elsif ( /\A--changeformat=(.*)\Z/ ) {
$ changeformat = $ 1 ;
next ;
2023-03-18 07:25:44 +08:00
} elsif ( /\A--manpath=(.*)\Z/ ) {
$ manpath = $ 1 ;
next ;
2023-09-05 00:56:00 +08:00
} elsif ( /\A--rev=(.*)\Z/ ) {
$ gitrev = $ 1 ;
next ;
2022-06-16 11:25:36 +08:00
}
2021-03-17 01:46:03 +08:00
$ srcpath = $ _ , next if not defined $ srcpath ;
$ wikipath = $ _ , next if not defined $ wikipath ;
}
2022-06-16 11:25:36 +08:00
my $ default_optionsfname = '.wikiheaders-options' ;
$ default_optionsfname = "$srcpath/$default_optionsfname" if defined $ srcpath ;
if ( ( not defined $ optionsfname ) && ( - f $ default_optionsfname ) ) {
$ optionsfname = $ default_optionsfname ;
}
if ( defined $ optionsfname ) {
open OPTIONS , '<' , $ optionsfname or die ( "Failed to open options file '$optionsfname': $!\n" ) ;
while ( <OPTIONS> ) {
2024-05-14 22:19:34 +08:00
next if /\A\s*\#/ ; # Skip lines that start with (optional whitespace, then) '#' as comments.
2022-06-16 11:25:36 +08:00
chomp ;
if ( /\A(.*?)\=(.*)\Z/ ) {
my $ key = $ 1 ;
my $ val = $ 2 ;
$ key =~ s/\A\s+// ;
$ key =~ s/\s+\Z// ;
$ val =~ s/\A\s+// ;
$ val =~ s/\s+\Z// ;
$ warn_about_missing = int ( $ val ) , next if $ key eq 'warn_about_missing' ;
$ srcpath = $ val , next if $ key eq 'srcpath' ;
$ wikipath = $ val , next if $ key eq 'wikipath' ;
$ apiprefixregex = $ val , next if $ key eq 'apiprefixregex' ;
$ projectfullname = $ val , next if $ key eq 'projectfullname' ;
$ projectshortname = $ val , next if $ key eq 'projectshortname' ;
$ wikisubdir = $ val , next if $ key eq 'wikisubdir' ;
$ incsubdir = $ val , next if $ key eq 'incsubdir' ;
2023-03-01 00:37:46 +08:00
$ readmesubdir = $ val , next if $ key eq 'readmesubdir' ;
2022-06-16 11:25:36 +08:00
$ versionmajorregex = $ val , next if $ key eq 'versionmajorregex' ;
$ versionminorregex = $ val , next if $ key eq 'versionminorregex' ;
2024-05-14 22:47:13 +08:00
$ versionmicroregex = $ val , next if $ key eq 'versionmicroregex' ;
2022-06-16 11:25:36 +08:00
$ versionfname = $ val , next if $ key eq 'versionfname' ;
$ mainincludefname = $ val , next if $ key eq 'mainincludefname' ;
$ selectheaderregex = $ val , next if $ key eq 'selectheaderregex' ;
$ projecturl = $ val , next if $ key eq 'projecturl' ;
$ wikiurl = $ val , next if $ key eq 'wikiurl' ;
$ bugreporturl = $ val , next if $ key eq 'bugreporturl' ;
2022-06-18 02:39:50 +08:00
$ wikipreamble = $ val , next if $ key eq 'wikipreamble' ;
2024-04-01 00:56:00 +08:00
$ wikiheaderfiletext = $ val , next if $ key eq 'wikiheaderfiletext' ;
$ manpageheaderfiletext = $ val , next if $ key eq 'manpageheaderfiletext' ;
2024-05-14 22:19:34 +08:00
$ headercategoryeval = $ val , next if $ key eq 'headercategoryeval' ;
2022-06-16 11:25:36 +08:00
}
}
close ( OPTIONS ) ;
}
2024-05-05 14:03:44 +08:00
sub escLaTeX {
my $ str = shift ;
$ str =~ s/([_\#\&\^])/\\$1/g ;
return $ str ;
}
2021-03-24 22:45:27 +08:00
my $ wordwrap_mode = 'mediawiki' ;
sub wordwrap_atom { # don't call this directly.
my $ str = shift ;
2022-05-25 22:42:11 +08:00
my $ retval = '' ;
# wordwrap but leave links intact, even if they overflow.
if ( $ wordwrap_mode eq 'mediawiki' ) {
while ( $ str =~ s/(.*?)\s*(\[https?\:\/\/.*?\s+.*?\])\s*//ms ) {
$ retval . = fill ( '' , '' , $ 1 ) ; # wrap it.
$ retval . = "\n$2\n" ; # don't wrap it.
}
} elsif ( $ wordwrap_mode eq 'md' ) {
while ( $ str =~ s/(.*?)\s*(\[.*?\]\(https?\:\/\/.*?\))\s*//ms ) {
$ retval . = fill ( '' , '' , $ 1 ) ; # wrap it.
$ retval . = "\n$2\n" ; # don't wrap it.
}
}
return $ retval . fill ( '' , '' , $ str ) ;
2021-03-24 22:45:27 +08:00
}
sub wordwrap_with_bullet_indent { # don't call this directly.
my $ bullet = shift ;
my $ str = shift ;
my $ retval = '' ;
2021-07-14 22:03:31 +08:00
#print("WORDWRAP BULLET ('$bullet'):\n\n$str\n\n");
2021-03-24 22:45:27 +08:00
# You _can't_ (at least with Pandoc) have a bullet item with a newline in
# MediaWiki, so _remove_ wrapping!
if ( $ wordwrap_mode eq 'mediawiki' ) {
$ retval = "$bullet$str" ;
$ retval =~ s/\n/ /gms ;
2021-07-14 22:03:31 +08:00
$ retval =~ s/\s+$//gms ;
#print("WORDWRAP BULLET DONE:\n\n$retval\n\n");
2021-03-24 22:45:27 +08:00
return "$retval\n" ;
}
my $ bulletlen = length ( $ bullet ) ;
# wrap it and then indent each line to be under the bullet.
$ Text:: Wrap:: columns -= $ bulletlen ;
my @ wrappedlines = split /\n/ , wordwrap_atom ( $ str ) ;
$ Text:: Wrap:: columns += $ bulletlen ;
my $ prefix = $ bullet ;
my $ usual_prefix = ' ' x $ bulletlen ;
foreach ( @ wrappedlines ) {
2023-02-24 08:53:53 +08:00
s/\s*\Z// ;
2021-03-24 22:45:27 +08:00
$ retval . = "$prefix$_\n" ;
$ prefix = $ usual_prefix ;
}
return $ retval ;
}
sub wordwrap_one_paragraph { # don't call this directly.
my $ retval = '' ;
my $ p = shift ;
#print "\n\n\nPARAGRAPH: [$p]\n\n\n";
if ( $ p =~ s/\A([\*\-] )// ) { # bullet list, starts with "* " or "- ".
my $ bullet = $ 1 ;
my $ item = '' ;
my @ items = split /\n/ , $ p ;
foreach ( @ items ) {
if ( s/\A([\*\-] )// ) {
$ retval . = wordwrap_with_bullet_indent ( $ bullet , $ item ) ;
$ item = '' ;
}
s/\A\s*// ;
$ item . = "$_\n" ; # accumulate lines until we hit the end or another bullet.
}
if ( $ item ne '' ) {
$ retval . = wordwrap_with_bullet_indent ( $ bullet , $ item ) ;
}
2024-04-09 11:48:25 +08:00
} 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...)
2021-03-24 22:45:27 +08:00
} else {
$ retval = wordwrap_atom ( $ p ) . "\n" ;
}
return $ retval ;
}
sub wordwrap_paragraphs { # don't call this directly.
my $ str = shift ;
my $ retval = '' ;
my @ paragraphs = split /\n\n/ , $ str ;
foreach ( @ paragraphs ) {
next if $ _ eq '' ;
$ retval . = wordwrap_one_paragraph ( $ _ ) ;
$ retval . = "\n" ;
}
return $ retval ;
}
2021-03-17 01:46:03 +08:00
my $ wordwrap_default_columns = 76 ;
sub wordwrap {
my $ str = shift ;
my $ columns = shift ;
$ columns = $ wordwrap_default_columns if not defined $ columns ;
$ columns += $ wordwrap_default_columns if $ columns < 0 ;
$ Text:: Wrap:: columns = $ columns ;
my $ retval = '' ;
2021-07-14 20:11:18 +08:00
#print("\n\nWORDWRAP:\n\n$str\n\n\n");
2021-07-14 22:03:31 +08:00
$ str =~ s/\A\n+//ms ;
2021-07-14 20:11:18 +08:00
while ( $ str =~ s/(.*?)(\`\`\`.*?\`\`\`|\<syntaxhighlight.*?\<\/syntaxhighlight\>)//ms ) {
#print("\n\nWORDWRAP BLOCK:\n\n$1\n\n ===\n\n$2\n\n\n");
2021-03-24 22:45:27 +08:00
$ retval . = wordwrap_paragraphs ( $ 1 ) ; # wrap it.
2021-07-14 20:11:18 +08:00
$ retval . = "$2\n\n" ; # don't wrap it.
2021-03-17 01:46:03 +08:00
}
2021-07-13 23:11:33 +08:00
$ retval . = wordwrap_paragraphs ( $ str ) ; # wrap what's left.
2021-07-14 23:58:57 +08:00
$ retval =~ s/\n+\Z//ms ;
2021-07-14 20:11:18 +08:00
#print("\n\nWORDWRAP DONE:\n\n$retval\n\n\n");
2021-07-13 23:11:33 +08:00
return $ retval ;
2021-03-17 01:46:03 +08:00
}
2021-07-14 20:11:18 +08:00
# This assumes you're moving from Markdown (in the Doxygen data) to Wiki, which
# is why the 'md' section is so sparse.
sub wikify_chunk {
2021-03-17 01:46:03 +08:00
my $ wikitype = shift ;
my $ str = shift ;
2021-07-14 20:11:18 +08:00
my $ codelang = shift ;
my $ code = shift ;
#print("\n\nWIKIFY CHUNK:\n\n$str\n\n\n");
2021-03-17 01:46:03 +08:00
if ( $ wikitype eq 'mediawiki' ) {
2021-10-01 05:34:25 +08:00
# convert `code` things first, so they aren't mistaken for other markdown items.
my $ codedstr = '' ;
while ( $ str =~ s/\A(.*?)\`(.*?)\`//ms ) {
my $ codeblock = $ 2 ;
$ codedstr . = wikify_chunk ( $ wikitype , $ 1 , undef , undef ) ;
2022-06-16 11:25:36 +08:00
if ( defined $ apiprefixregex ) {
# Convert obvious API things to wikilinks, even inside `code` blocks.
2024-10-05 03:33:41 +08:00
$ codeblock =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[\[$2\]\]/gms ;
2022-06-16 11:25:36 +08:00
}
2021-10-01 05:34:25 +08:00
$ codedstr . = "<code>$codeblock</code>" ;
}
2022-06-16 11:25:36 +08:00
# Convert obvious API things to wikilinks.
if ( defined $ apiprefixregex ) {
2024-10-05 03:33:41 +08:00
$ str =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[\[$2\]\]/gms ;
2022-06-16 11:25:36 +08:00
}
2021-03-17 01:46:03 +08:00
# Make some Markdown things into MediaWiki...
2022-05-25 22:42:11 +08:00
# links
$ str =~ s/\[(.*?)\]\((https?\:\/\/.*?)\)/\[$2 $1\]/g ;
2021-03-17 01:46:03 +08:00
# bold+italic
2021-03-26 00:52:15 +08:00
$ str =~ s/\*\*\*(.*?)\*\*\*/'''''$1'''''/gms ;
2021-03-17 01:46:03 +08:00
# bold
$ str =~ s/\*\*(.*?)\*\*/'''$1'''/gms ;
# italic
$ str =~ s/\*(.*?)\*/''$1''/gms ;
2021-03-24 22:45:27 +08:00
# bullets
$ str =~ s/^\- /* /gm ;
2021-07-14 20:11:18 +08:00
2021-10-01 05:34:25 +08:00
$ str = $ codedstr . $ str ;
2021-07-14 20:11:18 +08:00
if ( defined $ code ) {
$ str . = "<syntaxhighlight lang='$codelang'>$code<\/syntaxhighlight>" ;
}
2021-03-17 01:46:03 +08:00
} elsif ( $ wikitype eq 'md' ) {
2023-02-28 12:07:43 +08:00
# convert `code` things first, so they aren't mistaken for other markdown items.
my $ codedstr = '' ;
while ( $ str =~ s/\A(.*?)(\`.*?\`)//ms ) {
my $ codeblock = $ 2 ;
$ codedstr . = wikify_chunk ( $ wikitype , $ 1 , undef , undef ) ;
if ( defined $ apiprefixregex ) {
# Convert obvious API things to wikilinks, even inside `code` blocks,
# BUT ONLY IF the entire code block is the API thing,
# So something like "just call `SDL_Whatever`" will become
# "just call [`SDL_Whatever`](SDL_Whatever)", but
# "just call `SDL_Whatever(7)`" will not. It's just the safest
# way to do this without resorting to wrapping things in html <code> tags.
$ codeblock =~ s/\A\`($apiprefixregex[a-zA-Z0-9_]+)\`\Z/[`$1`]($1)/gms ;
}
$ codedstr . = $ codeblock ;
}
# Convert obvious API things to wikilinks.
2022-06-16 11:25:36 +08:00
if ( defined $ apiprefixregex ) {
2024-10-05 03:33:41 +08:00
$ str =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[$2\]\($2\)/gms ;
2022-06-16 11:25:36 +08:00
}
2023-02-28 12:07:43 +08:00
$ str = $ codedstr . $ str ;
2021-07-14 20:11:18 +08:00
if ( defined $ code ) {
2024-05-08 02:23:02 +08:00
$ str . = "```$codelang\n$code\n```\n" ;
2021-07-14 20:11:18 +08:00
}
2021-03-17 01:46:03 +08:00
}
2021-07-14 20:11:18 +08:00
#print("\n\nWIKIFY CHUNK DONE:\n\n$str\n\n\n");
2021-03-17 01:46:03 +08:00
return $ str ;
}
2021-07-14 20:11:18 +08:00
sub wikify {
2021-03-17 01:46:03 +08:00
my $ wikitype = shift ;
my $ str = shift ;
2021-07-14 20:11:18 +08:00
my $ retval = '' ;
2021-03-17 01:46:03 +08:00
2021-07-14 20:11:18 +08:00
#print("WIKIFY WHOLE:\n\n$str\n\n\n");
2021-03-17 01:46:03 +08:00
2024-05-08 02:23:02 +08:00
while ( $ str =~ s/\A(.*?)\`\`\`(.*?)\n(.*?)\n\`\`\`(\n|\Z)//ms ) {
2021-07-14 20:11:18 +08:00
$ retval . = wikify_chunk ( $ wikitype , $ 1 , $ 2 , $ 3 ) ;
2021-03-17 01:46:03 +08:00
}
2021-07-14 20:11:18 +08:00
$ retval . = wikify_chunk ( $ wikitype , $ str , undef , undef ) ;
2021-03-17 01:46:03 +08:00
2021-07-14 20:11:18 +08:00
#print("WIKIFY WHOLE DONE:\n\n$retval\n\n\n");
2021-03-17 01:46:03 +08:00
2021-07-14 20:11:18 +08:00
return $ retval ;
}
2022-01-07 04:37:05 +08:00
my $ dewikify_mode = 'md' ;
my $ dewikify_manpage_code_indent = 1 ;
2021-07-14 20:11:18 +08:00
sub dewikify_chunk {
my $ wikitype = shift ;
my $ str = shift ;
my $ codelang = shift ;
my $ code = shift ;
#print("\n\nDEWIKIFY CHUNK:\n\n$str\n\n\n");
2021-03-17 01:46:03 +08:00
2022-01-07 04:37:05 +08:00
if ( $ dewikify_mode eq 'md' ) {
if ( $ wikitype eq 'mediawiki' ) {
# Doxygen supports Markdown (and it just simply looks better than MediaWiki
# when looking at the raw headers), so do some conversions here as necessary.
2021-03-17 01:46:03 +08:00
2022-06-16 11:25:36 +08:00
# Dump obvious wikilinks.
if ( defined $ apiprefixregex ) {
$ str =~ s/\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]/$1/gms ;
}
2021-03-17 01:46:03 +08:00
2022-05-25 22:42:11 +08:00
# links
$ str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\[$2\]\($1\)/g ;
2022-01-07 04:37:05 +08:00
# <code></code> is also popular. :/
$ str =~ s/\<code>(.*?)<\/code>/`$1`/gms ;
2021-03-17 01:46:03 +08:00
2022-01-07 04:37:05 +08:00
# bold+italic
$ str =~ s/'''''(.*?)'''''/***$1***/gms ;
2021-03-17 01:46:03 +08:00
2022-01-07 04:37:05 +08:00
# bold
$ str =~ s/'''(.*?)'''/**$1**/gms ;
2021-03-17 01:46:03 +08:00
2022-01-07 04:37:05 +08:00
# italic
$ str =~ s/''(.*?)''/*$1*/gms ;
2021-03-24 22:45:27 +08:00
2022-01-07 04:37:05 +08:00
# bullets
$ str =~ s/^\* /- /gm ;
2023-02-24 08:53:53 +08:00
} elsif ( $ wikitype eq 'md' ) {
# Dump obvious wikilinks. The rest can just passthrough.
if ( defined $ apiprefixregex ) {
2023-02-28 12:07:43 +08:00
$ str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/$1/gms ;
2023-02-24 08:53:53 +08:00
}
2022-01-07 04:37:05 +08:00
}
if ( defined $ code ) {
2024-05-08 02:23:02 +08:00
$ str . = "\n```$codelang\n$code\n```\n" ;
2022-01-07 04:37:05 +08:00
}
} elsif ( $ dewikify_mode eq 'manpage' ) {
$ str =~ s/\./\\[char46]/gms ; # make sure these can't become control codes.
if ( $ wikitype eq 'mediawiki' ) {
2022-06-16 11:25:36 +08:00
# Dump obvious wikilinks.
if ( defined $ apiprefixregex ) {
$ str =~ s/\s*\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]\s*/\n.BR $1\n/gms ;
}
2022-01-07 04:37:05 +08:00
2022-05-25 22:42:11 +08:00
# links
$ str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\n.URL "$1" "$2"\n/g ;
2022-01-07 04:37:05 +08:00
# <code></code> is also popular. :/
$ str =~ s/\s*\<code>(.*?)<\/code>\s*/\n.BR $1\n/gms ;
2023-03-22 13:23:06 +08:00
# bold+italic (this looks bad, just make it bold).
$ str =~ s/\s*'''''(.*?)'''''\s*/\n.B $1\n/gms ;
2021-03-17 01:46:03 +08:00
2022-01-07 04:37:05 +08:00
# bold
$ str =~ s/\s*'''(.*?)'''\s*/\n.B $1\n/gms ;
# italic
$ str =~ s/\s*''(.*?)''\s*/\n.I $1\n/gms ;
# bullets
$ str =~ s/^\* /\n\\\(bu /gm ;
2023-02-24 23:21:32 +08:00
} elsif ( $ wikitype eq 'md' ) {
# Dump obvious wikilinks.
if ( defined $ apiprefixregex ) {
2023-02-28 12:07:43 +08:00
$ str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/\n.BR $1\n/gms ;
2023-02-24 23:21:32 +08:00
}
# links
$ str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\n.URL "$2" "$1"\n/g ;
# <code></code> is also popular. :/
$ str =~ s/\s*\`(.*?)\`\s*/\n.BR $1\n/gms ;
2023-03-22 13:23:06 +08:00
# bold+italic (this looks bad, just make it bold).
$ str =~ s/\s*\*\*\*(.*?)\*\*\*\s*/\n.B $1\n/gms ;
2023-02-24 23:21:32 +08:00
# bold
$ str =~ s/\s*\*\*(.*?)\*\*\s*/\n.B $1\n/gms ;
# italic
$ str =~ s/\s*\*(.*?)\*\s*/\n.I $1\n/gms ;
# bullets
$ str =~ s/^\- /\n\\\(bu /gm ;
2022-01-07 04:37:05 +08:00
}
if ( defined $ code ) {
$ code =~ s/\A\n+//gms ;
$ code =~ s/\n+\Z//gms ;
if ( $ dewikify_manpage_code_indent ) {
$ str . = "\n.IP\n"
} else {
$ str . = "\n.PP\n"
}
$ str . = ".EX\n$code\n.EE\n.PP\n" ;
}
2024-05-05 14:03:44 +08:00
} 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" ;
}
}
2022-01-07 04:37:05 +08:00
} else {
2024-04-09 10:32:36 +08:00
die ( "Unexpected dewikify_mode" ) ;
2021-07-14 20:11:18 +08:00
}
#print("\n\nDEWIKIFY CHUNK DONE:\n\n$str\n\n\n");
2021-03-17 01:46:03 +08:00
return $ str ;
}
2021-07-14 20:11:18 +08:00
sub dewikify {
my $ wikitype = shift ;
my $ str = shift ;
return '' if not defined $ str ;
#print("DEWIKIFY WHOLE:\n\n$str\n\n\n");
$ str =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms ;
$ str =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms ;
my $ retval = '' ;
2024-05-05 14:03:44 +08:00
if ( $ wikitype eq 'mediawiki' ) {
while ( $ str =~ s/\A(.*?)<syntaxhighlight lang='?(.*?)'?>(.*?)<\/syntaxhighlight\>//ms ) {
$ retval . = dewikify_chunk ( $ wikitype , $ 1 , $ 2 , $ 3 ) ;
}
} elsif ( $ wikitype eq 'md' ) {
2024-05-08 02:23:02 +08:00
while ( $ str =~ s/\A(.*?)\n```(.*?)\n(.*?)\n```\n//ms ) {
$ retval . = dewikify_chunk ( $ wikitype , $ 1 , $ 2 , $ 3 ) ;
2024-05-05 14:03:44 +08:00
}
2021-07-14 20:11:18 +08:00
}
$ retval . = dewikify_chunk ( $ wikitype , $ str , undef , undef ) ;
#print("DEWIKIFY WHOLE DONE:\n\n$retval\n\n\n");
return $ retval ;
}
2023-03-01 01:03:48 +08:00
sub filecopy {
my $ src = shift ;
my $ dst = shift ;
my $ endline = shift ;
$ endline = "\n" if not defined $ endline ;
open ( COPYIN , '<' , $ src ) or die ( "Failed to open '$src' for reading: $!\n" ) ;
open ( COPYOUT , '>' , $ dst ) or die ( "Failed to open '$dst' for writing: $!\n" ) ;
while ( <COPYIN> ) {
chomp ;
s/[ \t\r\n]*\Z// ;
print COPYOUT "$_$endline" ;
}
close ( COPYOUT ) ;
close ( COPYIN ) ;
}
2021-03-17 01:46:03 +08:00
sub usage {
2023-03-18 07:25:44 +08:00
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" ) ;
2021-03-17 01:46:03 +08:00
}
usage ( ) if not defined $ srcpath ;
usage ( ) if not defined $ wikipath ;
#usage() if $copy_direction == 0;
2023-03-22 12:56:34 +08:00
if ( not defined $ manpath ) {
$ manpath = "$srcpath/man" ;
}
2021-03-17 01:46:03 +08:00
my @ standard_wiki_sections = (
'Draft' ,
'[Brief]' ,
2021-10-24 02:37:47 +08:00
'Deprecated' ,
2024-04-01 00:56:00 +08:00
'Header File' ,
2021-03-17 01:46:03 +08:00
'Syntax' ,
'Function Parameters' ,
2024-04-09 10:34:11 +08:00
'Macro Parameters' ,
'Fields' ,
'Values' ,
2021-03-17 01:46:03 +08:00
'Return Value' ,
2021-03-24 22:46:05 +08:00
'Remarks' ,
2023-01-25 11:13:25 +08:00
'Thread Safety' ,
2021-03-17 01:46:03 +08:00
'Version' ,
2021-03-24 22:46:05 +08:00
'Code Examples' ,
2024-04-07 11:25:37 +08:00
'See Also'
2021-03-17 01:46:03 +08:00
) ;
2021-03-24 22:46:05 +08:00
# 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 ,
2024-04-01 00:56:00 +08:00
'Code Examples' , 1 ,
'Header File' , 1
2021-03-24 22:46:05 +08:00
) ;
2021-03-17 01:46:03 +08:00
my % headers = ( ) ; # $headers{"SDL_audio.h"} -> reference to an array of all lines of text in SDL_audio.h.
2024-04-06 14:10:42 +08:00
my % headersyms = ( ) ; # $headersyms{"SDL_OpenAudio"} -> string of header documentation for SDL_OpenAudio, with comment '*' bits stripped from the start. Newlines embedded!
2021-03-17 01:46:03 +08:00
my % headerdecls = ( ) ;
2024-04-06 14:10:42 +08:00
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.
2024-04-11 12:36:10 +08:00
my % headersymstype = ( ) ; # $headersymstype{"SDL_OpenAudio"} -> 1 (function), 2 (macro), 3 (struct), 4 (enum), 5 (other typedef)
2024-05-14 22:19:34 +08:00
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.
2024-05-16 22:44:37 +08:00
my % headercategorydocs = ( ) ; # $headercategorydocs{"Audio"} -> (fake) symbol for this category's documentation. Undefined if not documented.
2024-06-13 03:49:59 +08:00
my % headersymsparaminfo = ( ) ; # $headersymsparaminfo{"SDL_OpenAudio"} -> reference to array of parameters, pushed by name, then C type string, repeating. Undef'd if void params, or not a function.
my % headersymsrettype = ( ) ; # $headersymsrettype{"SDL_OpenAudio"} -> string of C datatype of return value. Undef'd if not a function.
2024-04-06 14:10:42 +08:00
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'
2024-05-01 10:47:02 +08:00
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.
2024-05-02 07:58:21 +08:00
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" ;
}
}
2024-04-06 14:10:42 +08:00
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" ;
}
}
2021-03-17 01:46:03 +08:00
2024-06-13 03:49:59 +08:00
sub strip_fn_declaration_metadata {
my $ decl = shift ;
$ decl =~ s/SDL_(PRINTF|SCANF)_FORMAT_STRING\s*// ; # don't want this metadata as part of the documentation.
$ decl =~ s/SDL_ALLOC_SIZE2?\(.*?\)\s*// ; # don't want this metadata as part of the documentation.
$ decl =~ s/SDL_MALLOC\s*// ; # don't want this metadata as part of the documentation.
$ decl =~ s/SDL_(IN|OUT|INOUT)_.*?CAP\s*\(.*?\)\s*//g ; # don't want this metadata as part of the documentation.
$ decl =~ s/\)(\s*SDL_[a-zA-Z_]+(\(.*?\)|))*;/);/ ; # don't want this metadata as part of the documentation.
return $ decl ;
}
sub sanitize_c_typename {
my $ str = shift ;
$ str =~ s/\A\s+// ;
$ str =~ s/\s+\Z// ;
$ str =~ s/const\s*(\*+)/const $1/g ; # one space between `const` and pointer stars: `char const* const *` becomes `char const * const *`.
$ str =~ s/\*\s+\*/**/g ; # drop spaces between pointers: `void * *` becomes `void **`.
$ str =~ s/\s*(\*+)\Z/ $1/ ; # one space between pointer stars and what it points to: `void**` becomes `void **`.
return $ str ;
}
2022-06-16 11:25:36 +08:00
my $ incpath = "$srcpath" ;
$ incpath . = "/$incsubdir" if $ incsubdir ne '' ;
2023-03-01 00:37:46 +08:00
my $ wikireadmepath = "$wikipath/$wikireadmesubdir" ;
my $ readmepath = undef ;
if ( defined $ readmesubdir ) {
$ readmepath = "$srcpath/$readmesubdir" ;
}
2021-03-17 01:46:03 +08:00
opendir ( DH , $ incpath ) or die ( "Can't opendir '$incpath': $!\n" ) ;
2023-03-25 04:57:24 +08:00
while ( my $ d = readdir ( DH ) ) {
my $ dent = $ d ;
2022-06-16 11:25:36 +08:00
next if not $ dent =~ /$selectheaderregex/ ; # just selected headers.
2021-03-17 01:46:03 +08:00
open ( FH , '<' , "$incpath/$dent" ) or die ( "Can't open '$incpath/$dent': $!\n" ) ;
2024-05-14 22:19:34 +08:00
# 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");
}
2021-03-17 01:46:03 +08:00
my @ contents = ( ) ;
2024-04-14 10:55:23 +08:00
my $ ignoring_lines = 0 ;
2024-05-02 07:58:21 +08:00
my $ header_comment = - 1 ;
2024-05-16 22:44:37 +08:00
my $ saw_category_doxygen = - 1 ;
2024-05-02 07:58:21 +08:00
my $ lineno = 0 ;
2021-03-17 01:46:03 +08:00
while ( <FH> ) {
chomp ;
2024-05-02 07:58:21 +08:00
$ lineno + + ;
2024-04-06 14:10:42 +08:00
my $ symtype = 0 ; # nothing, yet.
2021-10-09 02:39:28 +08:00
my $ decl ;
my @ templines ;
my $ str ;
my $ has_doxygen = 1 ;
2024-04-06 14:10:42 +08:00
# 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!
2024-05-02 07:58:21 +08:00
if ( $ header_comment == - 1 ) {
$ header_comment = /\A\/\*\s*\Z/ ? 1 : 0 ;
} elsif ( ( $ header_comment == 1 ) && ( /\A\*\/\s*\Z/ ) ) {
$ header_comment = 0 ;
}
2024-04-14 10:55:23 +08:00
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 ;
2024-05-14 22:19:34 +08:00
} 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 ;
2024-06-13 04:54:19 +08:00
} elsif ( /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/ ) { # a function declaration without a doxygen comment?
2024-04-06 14:10:42 +08:00
$ symtype = 1 ; # function declaration
2021-10-09 02:39:28 +08:00
@ templines = ( ) ;
$ decl = $ _ ;
$ str = '' ;
$ has_doxygen = 0 ;
2024-04-14 08:08:14 +08:00
} 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 ;
2021-10-09 02:39:28 +08:00
} elsif ( not /\A\/\*\*\s*\Z/ ) { # not doxygen comment start?
2021-03-17 01:46:03 +08:00
push @ contents , $ _ ;
2024-05-02 07:58:21 +08:00
add_coverage_gap ( $ _ , $ dent , $ lineno ) if ( $ header_comment == 0 ) ;
2021-03-17 01:46:03 +08:00
next ;
2021-10-09 02:39:28 +08:00
} else { # Start of a doxygen comment, parse it out.
2024-05-16 22:44:37 +08:00
my $ is_category_doxygen = 0 ;
2021-10-09 02:39:28 +08:00
@ templines = ( $ _ ) ;
while ( <FH> ) {
chomp ;
2024-05-02 07:58:21 +08:00
$ lineno + + ;
2021-10-09 02:39:28 +08:00
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 ;
2024-05-02 07:58:21 +08:00
$ lineno + + ;
2021-10-09 02:39:28 +08:00
push @ templines , $ _ ;
s/\A\s*\*\s?// ;
if ( s/\A\s*\`\`\`/```/ ) {
$ str . = "$_\n" ;
last ;
} else {
$ str . = "$_\n" ;
}
2021-07-14 20:11:18 +08:00
}
2021-10-09 02:39:28 +08:00
} else {
2024-05-16 22:44:37 +08:00
s/\A\s*\*\s*// ; # Strip off the " * " at the start of the comment line.
# To add documentation to Category Pages, the rule is it has to
# be the first Doxygen comment in the header, and it must start with `# CategoryX`
# (otherwise we'll treat it as documentation for whatever's below it). `X` is
# the category name, which doesn't _necessarily_ have to match
# $current_wiki_category, but it probably should.
#
# For compatibility with Doxygen, if there's a `\file` here instead of
# `# CategoryName`, we'll accept it and use the $current_wiki_category if set.
if ( $ saw_category_doxygen == - 1 ) {
$ saw_category_doxygen = defined ( $ current_wiki_category ) && /\A\\file\s+/ ;
if ( $ saw_category_doxygen ) {
$ _ = "# Category$current_wiki_category" ;
} else {
$ saw_category_doxygen = /\A\# Category/ ;
}
$ is_category_doxygen = $ saw_category_doxygen ;
}
2021-10-09 02:39:28 +08:00
$ str . = "$_\n" ;
2021-07-14 20:11:18 +08:00
}
}
2021-03-17 01:46:03 +08:00
2024-05-16 22:44:37 +08:00
if ( $ is_category_doxygen ) {
$ str =~ s/\s*\Z// ;
$ decl = '' ;
$ symtype = - 1 ; # not a symbol at all.
2024-04-06 14:10:42 +08:00
} else {
2024-05-16 22:44:37 +08:00
$ decl = <FH> ;
$ lineno + + if defined $ decl ;
$ decl = '' if not defined $ decl ;
chomp ( $ decl ) ;
2024-06-13 04:54:19 +08:00
if ( $ decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/ ) {
2024-05-16 22:44:37 +08:00
$ 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
2024-06-15 23:31:17 +08:00
} elsif ( $ decl =~ /\A\s*(typedef\s+|)(struct|union)\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\Z)/ ) {
2024-05-16 22:44:37 +08:00
$ symtype = 3 ; # struct or union
2024-06-15 23:31:17 +08:00
} elsif ( $ decl =~ /\A\s*(typedef\s+|)enum\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\Z)/ ) {
2024-05-16 22:44:37 +08:00
$ 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 ;
2021-10-09 02:39:28 +08:00
}
2021-03-17 01:46:03 +08:00
}
}
2024-06-13 03:49:59 +08:00
my @ paraminfo = ( ) ;
my $ rettype = undef ;
2021-03-17 01:46:03 +08:00
my @ decllines = ( $ decl ) ;
2024-04-06 14:10:42 +08:00
my $ sym = '' ;
2024-05-16 22:44:37 +08:00
if ( $ symtype == - 1 ) { # category documentation with no symbol attached.
@ decllines = ( ) ;
if ( $ str =~ /^#\s*Category(.*?)\s*$/m ) {
$ sym = "[category documentation] $1" ; # make a fake, unique symbol that's not valid C.
} else {
die ( "Unexpected category documentation line '$str' in '$incpath/$dent' ...?" ) ;
}
$ headercategorydocs { $ current_wiki_category } = $ sym ;
} elsif ( $ symtype == 1 ) { # a function
2024-04-14 08:08:14 +08:00
my $ is_forced_inline = ( $ decl =~ /\A\s*SDL_FORCE_INLINE/ ) ;
if ( $ is_forced_inline ) {
if ( not $ decl =~ /\)\s*(\{.*|)\s*\Z/ ) {
while ( <FH> ) {
chomp ;
2024-05-02 07:58:21 +08:00
$ lineno + + ;
2024-04-14 08:08:14 +08:00
push @ decllines , $ _ ;
s/\A\s+// ;
s/\s+\Z// ;
$ decl . = " $_" ;
last if /\)\s*(\{.*|)\s*\Z/ ;
}
2024-04-06 14:10:42 +08:00
}
2024-04-14 08:08:14 +08:00
$ decl =~ s/\s*\)\s*(\{.*|)\s*\Z/);/ ;
} else {
2024-06-11 23:29:44 +08:00
if ( not $ decl =~ /;/ ) {
2024-04-14 08:08:14 +08:00
while ( <FH> ) {
chomp ;
2024-05-02 07:58:21 +08:00
$ lineno + + ;
2024-04-14 08:08:14 +08:00
push @ decllines , $ _ ;
s/\A\s+// ;
s/\s+\Z// ;
$ decl . = " $_" ;
2024-06-11 23:29:44 +08:00
last if /;/ ;
2024-04-14 08:08:14 +08:00
}
}
$ decl =~ s/\s+\);\Z/);/ ;
2024-06-11 23:29:44 +08:00
$ decl =~ s/\s+;\Z/;/ ;
2021-03-17 01:46:03 +08:00
}
2024-04-06 14:10:42 +08:00
$ decl =~ s/\s+\Z// ;
2021-03-17 01:46:03 +08:00
2024-06-13 03:49:59 +08:00
$ decl = strip_fn_declaration_metadata ( $ decl ) ;
my $ paramsstr = undef ;
2024-06-11 23:29:44 +08:00
2024-07-26 21:05:57 +08:00
if ( ! $ is_forced_inline && $ decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/ ) {
2024-06-13 03:49:59 +08:00
$ sym = $ 8 ;
$ rettype = "$3$4$5$6" ;
$ paramsstr = $ 9 ;
} elsif ( $ is_forced_inline && $ decl =~ /\A\s*SDL_FORCE_INLINE\s+(SDL_DEPRECATED\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/ ) {
2024-04-14 08:08:14 +08:00
$ sym = $ 6 ;
2024-06-13 03:49:59 +08:00
$ rettype = "$2$3$4$5" ;
$ paramsstr = $ 7 ;
} else {
2024-04-06 14:10:42 +08:00
#print "Found doxygen but no function sig:\n$str\n\n";
foreach ( @ templines ) {
push @ contents , $ _ ;
}
foreach ( @ decllines ) {
push @ contents , $ _ ;
}
next ;
2021-03-17 01:46:03 +08:00
}
2024-04-06 14:10:42 +08:00
2024-06-13 03:49:59 +08:00
$ rettype = sanitize_c_typename ( $ rettype ) ;
if ( $ paramsstr =~ /\(/ ) {
die ( "\n\n$0 FAILURE!\n" .
"There's a '(' in the parameters for function '$sym' '$incpath/$dent'.\n" .
"This usually means there's a parameter that's a function pointer type.\n" .
"This causes problems for wikiheaders.pl and is less readable, too.\n" .
"Please put that function pointer into a typedef,\n" .
"and use the new type in this function's signature instead!\n\n" ) ;
}
my @ params = split ( /,/ , $ paramsstr ) ;
my $ dotdotdot = 0 ;
foreach ( @ params ) {
my $ p = $ _ ;
$ p =~ s/\A\s+// ;
$ p =~ s/\s+\Z// ;
if ( ( $ p eq 'void' ) || ( $ p eq '' ) ) {
die ( "Void parameter in a function with multiple params?! ('$sym' in '$incpath/$dent')" ) if ( scalar ( @ params ) != 1 ) ;
} elsif ( $ p eq '...' ) {
die ( "Mutiple '...' params?! ('$sym' in '$incpath/$dent')" ) if ( $ dotdotdot ) ;
$ dotdotdot = 1 ;
push @ paraminfo , '...' ;
push @ paraminfo , '...' ;
} elsif ( $ p =~ /\A(.*)\s+([a-zA-Z0-9_\*\[\]]+)\Z/ ) {
die ( "Parameter after '...' param?! ('$sym' in '$incpath/$dent')" ) if ( $ dotdotdot ) ;
my $ t = $ 1 ;
my $ n = $ 2 ;
if ( $ n =~ s/\A(\*+)// ) {
$ t . = $ 1 ; # move any `*` that stuck to the name over.
}
if ( $ n =~ s/\[\]\Z// ) {
$ t = "$t*" ; # move any `[]` that stuck to the name over, as a pointer.
}
$ t = sanitize_c_typename ( $ t ) ;
#print("$t\n");
#print("$n\n");
push @ paraminfo , $ n ;
push @ paraminfo , $ t ;
} else {
die ( "Unexpected parameter '$p' in function '$sym' in '$incpath/$dent'!" ) ;
}
}
2024-06-11 23:37:45 +08:00
if ( ! $ is_forced_inline ) { # don't do with forced-inline because we don't want the implementation inserted in the wiki.
2024-07-26 21:05:57 +08:00
my $ shrink_length = 0 ;
2024-06-11 23:37:45 +08:00
$ decl = '' ; # rebuild this with the line breaks, since it looks better for syntax highlighting.
2024-04-14 08:08:14 +08:00
foreach ( @ decllines ) {
if ( $ decl eq '' ) {
2024-07-26 21:05:57 +08:00
my $ temp ;
2024-04-14 08:08:14 +08:00
$ decl = $ _ ;
2024-07-26 21:05:57 +08:00
$ temp = $ decl ;
$ temp =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 / ;
$ shrink_length = length ( $ decl ) - length ( $ temp ) ;
$ decl = $ temp ;
2024-04-14 08:08:14 +08:00
} else {
my $ trimmed = $ _ ;
2024-07-26 21:05:57 +08:00
$ trimmed =~ s/\A\s{$shrink_length}// ; # shrink to match the removed "extern SDL_DECLSPEC SDLCALL "
2024-04-14 08:08:14 +08:00
$ decl . = $ trimmed ;
}
$ decl . = "\n" ;
2024-04-06 14:10:42 +08:00
}
2021-03-17 01:46:03 +08:00
}
2024-05-03 00:49:30 +08:00
2024-06-13 03:49:59 +08:00
$ decl = strip_fn_declaration_metadata ( $ decl ) ;
2024-06-11 22:58:02 +08:00
2024-05-03 00:49:30 +08:00
# !!! 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 ;
$ 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.
2024-05-04 01:16:17 +08:00
# 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 - - ;
}
2024-05-03 00:49:30 +08:00
}
2024-05-04 01:16:17 +08:00
$ additional_decl . = "\n$_" ;
push @ decllines , $ _ ;
$ lastpos = tell ( FH ) ;
2024-05-03 00:49:30 +08:00
}
} else {
seek ( FH , $ lastpos , 0 ) ; # re-read eaten lines again next time.
$ lineno = $ lastlineno ;
last ;
}
}
$ decl . = $ additional_decl ;
2024-04-06 14:10:42 +08:00
} elsif ( $ symtype == 2 ) { # a macro
if ( $ decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)\s+/ ) {
$ sym = $ 1 ;
2021-03-17 01:46:03 +08:00
} else {
2024-04-06 14:10:42 +08:00
#print "Found doxygen but no macro:\n$str\n\n";
foreach ( @ templines ) {
push @ contents , $ _ ;
}
foreach ( @ decllines ) {
push @ contents , $ _ ;
}
next ;
2021-03-17 01:46:03 +08:00
}
2024-04-06 14:10:42 +08:00
while ( $ decl =~ /\\\Z/ ) {
my $ l = <FH> ;
last if not $ l ;
2024-05-02 07:58:21 +08:00
$ lineno + + ;
2024-04-06 14:10:42 +08:00
chomp ( $ l ) ;
push @ decllines , $ l ;
#$l =~ s/\A\s+//;
$ l =~ s/\s+\Z// ;
$ decl . = "\n$l" ;
}
2024-04-09 10:34:11 +08:00
} elsif ( ( $ symtype == 3 ) || ( $ symtype == 4 ) ) { # struct or union or enum
my $ has_definition = 0 ;
2024-06-15 23:31:17 +08:00
if ( $ decl =~ /\A\s*(typedef\s+|)(struct|union|enum)\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\;|\Z)/ ) {
2024-04-09 10:34:11 +08:00
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.
2024-08-17 11:45:14 +08:00
# It also "parses" enums enough to find out the elements of it.
2024-04-09 10:34:11 +08:00
if ( $ has_definition ) {
my $ started = 0 ;
my $ brackets = 0 ;
my $ pending = $ decl ;
2024-08-17 11:45:14 +08:00
my $ skipping_comment = 0 ;
2024-04-09 10:34:11 +08:00
$ decl = '' ;
while ( ! $ started || ( $ brackets != 0 ) ) {
2024-08-17 11:45:14 +08:00
foreach my $ seg ( split ( /([{}])/ , $ pending ) ) { # (this will pick up brackets in comments! Be careful!)
2024-04-09 10:34:11 +08:00
$ 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 - - ;
}
}
2024-08-17 11:45:14 +08:00
if ( $ skipping_comment ) {
if ( $ pending =~ s/\A.*?\*\/// ) {
$ skipping_comment = 0 ;
}
}
if ( ! $ skipping_comment && $ started && ( $ symtype == 4 ) ) { # Pick out elements of an enum.
my $ stripped = "$pending" ;
$ stripped =~ s/\/\*.*?\*\///g ; # dump /* comments */ that exist fully on one line.
if ( $ stripped =~ /\/\*/ ) { # uhoh, a /* comment */ that crosses newlines.
$ skipping_comment = 1 ;
} elsif ( $ stripped =~ /\A\s*([a-zA-Z0-9_]+)(.*)\Z/ ) { #\s*(\=\s*.*?|)\s*,?(.*?)\Z/) {
if ( $ 1 ne 'typedef' ) { # make sure we didn't just eat the first line by accident. :/
#print("ENUM [$1] $incpath/$dent:$lineno\n");
$ referenceonly { $ 1 } = $ sym ;
}
}
}
2024-04-09 10:34:11 +08:00
if ( ! $ started || ( $ brackets != 0 ) ) {
$ pending = <FH> ;
die ( "EOF/error reading $incpath/$dent while parsing $sym\n" ) if not $ pending ;
2024-05-02 07:58:21 +08:00
$ lineno + + ;
2024-04-09 10:34:11 +08:00
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!
}
2024-04-11 12:36:10 +08:00
} elsif ( $ symtype == 5 ) { # other typedef
2024-05-04 00:58:07 +08:00
if ( $ decl =~ /\A\s*typedef\s+(.*)\Z/ ) {
2024-04-11 12:36:10 +08:00
my $ tdstr = $ 1 ;
2024-05-04 00:58:07 +08:00
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// ;
2024-04-11 12:36:10 +08:00
#my $datatype;
2024-05-04 00:58:07 +08:00
if ( $ tdstr =~ /\A(.*?)\s*\((.*?)\s*\*\s*(.*?)\)\s*\((.*?)(\))?/ ) { # a function pointer type
2024-04-11 12:36:10 +08:00
$ 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 {
#print "Found doxygen but no datatype:\n$str\n\n";
foreach ( @ templines ) {
push @ contents , $ _ ;
}
foreach ( @ decllines ) {
push @ contents , $ _ ;
}
next ;
}
2024-04-25 14:58:53 +08:00
2024-04-26 02:26:49 +08:00
# We assume any `#define`s directly after the typedef are related to it: probably bitflags for an integer typedef.
2024-04-26 04:46:43 +08:00
# We'll also allow some other basic preprocessor lines.
2024-04-26 02:26:49 +08:00
# Blank lines are allowed, anything else, even comments, are not.
my $ blank_lines = 0 ;
2024-04-25 14:58:53 +08:00
my $ lastpos = tell ( FH ) ;
2024-05-02 07:58:21 +08:00
my $ lastlineno = $ lineno ;
2024-04-25 14:58:53 +08:00
my $ additional_decl = '' ;
2024-05-03 00:49:30 +08:00
my $ saw_define = 0 ;
2024-04-25 14:58:53 +08:00
while ( <FH> ) {
chomp ;
2024-04-26 02:26:49 +08:00
2024-05-02 07:58:21 +08:00
$ lineno + + ;
2024-04-26 02:26:49 +08:00
if ( /\A\s*\Z/ ) {
$ blank_lines + + ;
2024-05-01 10:47:02 +08:00
} elsif ( /\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/ ) {
if ( /\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/ ) {
$ referenceonly { $ 1 } = $ sym ;
2024-05-03 00:49:30 +08:00
$ 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 ;
2024-05-01 10:47:02 +08:00
}
# update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
2024-04-26 02:26:49 +08:00
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.
2024-05-02 07:58:21 +08:00
$ lineno = $ lastlineno ;
2024-04-25 14:58:53 +08:00
last ;
}
}
2024-04-26 02:26:49 +08:00
$ decl . = $ additional_decl ;
2024-04-06 14:10:42 +08:00
} else {
2024-04-09 10:32:36 +08:00
die ( "Unexpected symtype $symtype" ) ;
2021-03-17 01:46:03 +08:00
}
2024-04-06 14:10:42 +08:00
#print("DECL: [$decl]\n");
#print("$sym:\n$str\n\n");
2021-07-14 20:11:18 +08:00
2021-10-09 08:49:51 +08:00
# 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.
2024-04-06 14:10:42 +08:00
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!
2021-10-09 08:49:51 +08:00
} else { # documented function already existed?
2024-04-06 14:10:42 +08:00
$ skipsym = 1 ; # don't add this copy to the list of functions.
2021-10-09 08:49:51 +08:00
if ( $ has_doxygen ) {
2024-04-06 14:10:42 +08:00
print STDERR "WARNING: Symbol '$sym' appears to be documented in multiple locations. Only keeping the first one we saw!\n" ;
2021-10-09 08:49:51 +08:00
}
2024-05-16 22:44:37 +08:00
push @ contents , join ( "\n" , @ decllines ) if ( scalar ( @ decllines ) > 0 ) ; # just put the existing declation in as-is.
2021-10-09 08:49:51 +08:00
}
}
2024-04-06 14:10:42 +08:00
if ( ! $ skipsym ) {
2024-05-14 22:19:34 +08:00
$ headersymscategory { $ sym } = $ current_wiki_category if defined $ current_wiki_category ;
2024-04-06 14:10:42 +08:00
$ headersyms { $ sym } = $ str ;
$ headerdecls { $ sym } = $ decl ;
$ headersymslocation { $ sym } = $ dent ;
$ headersymschunk { $ sym } = scalar ( @ contents ) ;
$ headersymshasdoxygen { $ sym } = $ has_doxygen ;
$ headersymstype { $ sym } = $ symtype ;
2024-06-13 03:49:59 +08:00
$ headersymsparaminfo { $ sym } = \ @ paraminfo if ( scalar ( @ paraminfo ) > 0 ) ;
$ headersymsrettype { $ sym } = $ rettype if ( defined ( $ rettype ) ) ;
2021-10-09 08:49:51 +08:00
push @ contents , join ( "\n" , @ templines ) ;
2024-05-16 22:44:37 +08:00
push @ contents , join ( "\n" , @ decllines ) if ( scalar ( @ decllines ) > 0 ) ;
2021-10-09 08:49:51 +08:00
}
2021-03-17 01:46:03 +08:00
}
close ( FH ) ;
$ headers { $ dent } = \ @ contents ;
}
closedir ( DH ) ;
2024-06-13 03:49:59 +08:00
2021-03-17 01:46:03 +08:00
opendir ( DH , $ wikipath ) or die ( "Can't opendir '$wikipath': $!\n" ) ;
2023-03-25 04:57:24 +08:00
while ( my $ d = readdir ( DH ) ) {
my $ dent = $ d ;
2021-03-17 01:46:03 +08:00
my $ type = '' ;
2022-06-16 11:25:36 +08:00
if ( $ dent =~ /\.(md|mediawiki)\Z/ ) {
2021-03-17 01:46:03 +08:00
$ type = $ 1 ;
} else {
next ; # only dealing with wiki pages.
}
2024-04-06 14:10:42 +08:00
my $ sym = $ dent ;
$ sym =~ s/\..*\Z// ;
2022-06-24 04:07:35 +08:00
2024-05-14 22:19:34 +08:00
# (There are other pages to ignore, but these are known ones to not bother parsing.)
2022-06-24 04:07:35 +08:00
# Ignore FrontPage.
2024-04-06 14:10:42 +08:00
next if $ sym eq 'FrontPage' ;
2022-06-24 04:07:35 +08:00
2021-03-17 01:46:03 +08:00
open ( FH , '<' , "$wikipath/$dent" ) or die ( "Can't open '$wikipath/$dent': $!\n" ) ;
2024-05-16 22:44:37 +08:00
if ( $ sym =~ /\ACategory(.*?)\Z/ ) { # Special case for Category pages.
# Find the end of the category documentation in the existing file and append everything else to the new file.
my $ cat = $ 1 ;
my $ docstr = '' ;
my $ notdocstr = '' ;
my $ docs = 1 ;
while ( <FH> ) {
chomp ;
if ( $ docs ) {
$ docs = 0 if /\A\-\-\-\-\Z/ ; # Hit a footer? We're done.
$ docs = 0 if /\A<!\-\-/ ; # Hit an HTML comment? We're done.
}
if ( $ docs ) {
$ docstr . = "$_\n" ;
} else {
$ notdocstr . = "$_\n" ;
}
}
close ( FH ) ;
$ docstr =~ s/\s*\Z// ;
$ sym = "[category documentation] $cat" ; # make a fake, unique symbol that's not valid C.
$ wikitypes { $ sym } = $ type ;
my % sections = ( ) ;
$ sections { 'Remarks' } = $ docstr ;
$ sections { '[footer]' } = $ notdocstr ;
$ wikisyms { $ sym } = \ % sections ;
my @ section_order = ( 'Remarks' , '[footer]' ) ;
$ wikisectionorder { $ sym } = \ @ section_order ;
next ;
}
2021-03-17 01:46:03 +08:00
my $ current_section = '[start]' ;
my @ section_order = ( $ current_section ) ;
my % sections = ( ) ;
$ sections { $ current_section } = '' ;
2022-06-18 02:39:50 +08:00
my $ firstline = 1 ;
2021-03-17 01:46:03 +08:00
while ( <FH> ) {
chomp ;
my $ orig = $ _ ;
s/\A\s*// ;
s/\s*\Z// ;
if ( $ type eq 'mediawiki' ) {
2022-06-18 02:39:50 +08:00
if ( defined ( $ wikipreamble ) && $ firstline && /\A\=\=\=\=\=\= (.*?) \=\=\=\=\=\=\Z/ && ( $ 1 eq $ wikipreamble ) ) {
$ firstline = 0 ; # skip this.
next ;
} elsif ( /\A\= (.*?) \=\Z/ ) {
$ firstline = 0 ;
2024-04-06 14:10:42 +08:00
$ current_section = ( $ 1 eq $ sym ) ? '[Brief]' : $ 1 ;
2021-03-17 01:46:03 +08:00
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/ ) {
2022-06-18 02:39:50 +08:00
$ firstline = 0 ;
2024-04-06 14:10:42 +08:00
$ current_section = ( $ 1 eq $ sym ) ? '[Brief]' : $ 1 ;
2021-03-17 01:46:03 +08:00
die ( "Doubly-defined section '$current_section' in '$dent'!\n" ) if defined $ sections { $ current_section } ;
push @ section_order , $ current_section ;
$ sections { $ current_section } = '' ;
next ;
} elsif ( /\A\-\-\-\-\Z/ ) {
2022-06-18 02:39:50 +08:00
$ firstline = 0 ;
2021-03-17 01:46:03 +08:00
$ current_section = '[footer]' ;
die ( "Doubly-defined section '$current_section' in '$dent'!\n" ) if defined $ sections { $ current_section } ;
push @ section_order , $ current_section ;
$ sections { $ current_section } = '' ;
next ;
}
} elsif ( $ type eq 'md' ) {
2022-06-18 02:39:50 +08:00
if ( defined ( $ wikipreamble ) && $ firstline && /\A\#\#\#\#\#\# (.*?)\Z/ && ( $ 1 eq $ wikipreamble ) ) {
$ firstline = 0 ; # skip this.
next ;
} elsif ( /\A\#+ (.*?)\Z/ ) {
$ firstline = 0 ;
2024-04-06 14:10:42 +08:00
$ current_section = ( $ 1 eq $ sym ) ? '[Brief]' : $ 1 ;
2021-03-17 01:46:03 +08:00
die ( "Doubly-defined section '$current_section' in '$dent'!\n" ) if defined $ sections { $ current_section } ;
push @ section_order , $ current_section ;
$ sections { $ current_section } = '' ;
next ;
} elsif ( /\A\-\-\-\-\Z/ ) {
2022-06-18 02:39:50 +08:00
$ firstline = 0 ;
2021-03-17 01:46:03 +08:00
$ current_section = '[footer]' ;
die ( "Doubly-defined section '$current_section' in '$dent'!\n" ) if defined $ sections { $ current_section } ;
push @ section_order , $ current_section ;
$ sections { $ current_section } = '' ;
next ;
}
} else {
2024-04-09 10:32:36 +08:00
die ( "Unexpected wiki file type. Fixme!" ) ;
2021-03-17 01:46:03 +08:00
}
2022-06-18 02:39:50 +08:00
if ( $ firstline ) {
$ firstline = ( $ _ ne '' ) ;
}
if ( ! $ firstline ) {
$ sections { $ current_section } . = "$orig\n" ;
}
2021-03-17 01:46:03 +08:00
}
close ( FH ) ;
2021-03-25 00:52:48 +08:00
foreach ( keys % sections ) {
$ sections { $ _ } =~ s/\A\n+// ;
$ sections { $ _ } =~ s/\n+\Z// ;
$ sections { $ _ } . = "\n" ;
}
2024-04-07 11:25:37 +08:00
# 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' } ;
}
2021-03-17 01:46:03 +08:00
if ( 0 ) {
foreach ( @ section_order ) {
2024-04-06 14:10:42 +08:00
print ( "$sym SECTION '$_':\n" ) ;
2021-03-17 01:46:03 +08:00
print ( $ sections { $ _ } ) ;
print ( "\n\n" ) ;
}
}
2024-04-06 14:10:42 +08:00
$ wikitypes { $ sym } = $ type ;
$ wikisyms { $ sym } = \ % sections ;
$ wikisectionorder { $ sym } = \ @ section_order ;
2021-03-17 01:46:03 +08:00
}
closedir ( DH ) ;
2024-04-06 14:10:42 +08:00
delete $ wikisyms { "Undocumented" } ;
2023-09-07 02:28:23 +08:00
{
my $ path = "$wikipath/Undocumented.md" ;
2024-04-06 14:10:42 +08:00
open ( my $ fh , '>' , $ path ) or die ( "Can't open '$path': $!\n" ) ;
2023-09-07 02:28:23 +08:00
2024-04-06 14:10:42 +08:00
print $ fh "# Undocumented\n\n" ;
print_undocumented_section ( $ fh , 'Functions' , 1 ) ;
2024-04-09 10:34:11 +08:00
#print_undocumented_section($fh, 'Macros', 2);
2023-09-07 02:28:23 +08:00
2024-04-06 14:10:42 +08:00
close ( $ fh ) ;
2023-09-07 02:28:23 +08:00
}
2021-03-17 01:46:03 +08:00
if ( $ warn_about_missing ) {
2024-04-06 14:10:42 +08:00
foreach ( keys % wikisyms ) {
my $ sym = $ _ ;
if ( not defined $ headersyms { $ sym } ) {
2024-06-13 03:49:59 +08:00
print STDERR "WARNING: $sym defined in the wiki but not the headers!\n" ;
2021-03-17 01:46:03 +08:00
}
}
2024-04-06 14:10:42 +08:00
foreach ( keys % headersyms ) {
my $ sym = $ _ ;
if ( not defined $ wikisyms { $ sym } ) {
2024-06-13 03:49:59 +08:00
print STDERR "WARNING: $sym defined in the headers but not the wiki!\n" ;
2021-03-17 01:46:03 +08:00
}
}
}
if ( $ copy_direction == 1 ) { # --copy-to-headers
my % changed_headers = ( ) ;
2021-03-26 00:50:18 +08:00
2022-01-07 04:37:05 +08:00
$ dewikify_mode = 'md' ;
2021-03-26 00:50:18 +08:00
$ wordwrap_mode = 'md' ; # the headers use Markdown format.
2024-04-06 14:10:42 +08:00
foreach ( keys % headersyms ) {
my $ sym = $ _ ;
next if not defined $ wikisyms { $ sym } ; # don't have a page for that function, skip it.
2024-04-09 10:34:11 +08:00
my $ symtype = $ headersymstype { $ sym } ;
2024-04-06 14:10:42 +08:00
my $ wikitype = $ wikitypes { $ sym } ;
my $ sectionsref = $ wikisyms { $ sym } ;
2023-03-25 04:57:24 +08:00
my $ remarks = $ sectionsref - > { 'Remarks' } ;
my $ returns = $ sectionsref - > { 'Return Value' } ;
my $ threadsafety = $ sectionsref - > { 'Thread Safety' } ;
my $ version = $ sectionsref - > { 'Version' } ;
2024-04-07 11:25:37 +08:00
my $ related = $ sectionsref - > { 'See Also' } ;
2023-03-25 04:57:24 +08:00
my $ deprecated = $ sectionsref - > { 'Deprecated' } ;
my $ brief = $ sectionsref - > { '[Brief]' } ;
2021-03-17 01:46:03 +08:00
my $ addblank = 0 ;
my $ str = '' ;
2024-04-09 10:34:11 +08:00
my $ params = undef ;
my $ paramstr = undef ;
2024-05-16 22:44:37 +08:00
if ( $ symtype == - 1 ) { # category documentation block.
# nothing to be done here.
} elsif ( ( $ symtype == 1 ) || ( ( $ symtype == 5 ) ) ) { # we'll assume a typedef (5) with a \param is a function pointer typedef.
2024-04-09 10:34:11 +08:00
$ params = $ sectionsref - > { 'Function Parameters' } ;
$ paramstr = '\param' ;
} elsif ( $ symtype == 2 ) {
$ params = $ sectionsref - > { 'Macro Parameters' } ;
$ paramstr = '\param' ;
} elsif ( $ symtype == 3 ) {
$ params = $ sectionsref - > { 'Fields' } ;
$ paramstr = '\field' ;
} elsif ( $ symtype == 4 ) {
$ params = $ sectionsref - > { 'Values' } ;
$ paramstr = '\value' ;
} else {
die ( "Unexpected symtype $symtype" ) ;
}
2024-04-06 14:10:42 +08:00
$ headersymshasdoxygen { $ sym } = 1 ; # Added/changed doxygen for this header.
2021-10-09 02:39:28 +08:00
2021-03-17 01:46:03 +08:00
$ brief = dewikify ( $ wikitype , $ brief ) ;
$ brief =~ s/\A(.*?\.) /$1\n/ ; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
my @ briefsplit = split /\n/ , $ brief ;
$ brief = shift @ briefsplit ;
if ( defined $ remarks ) {
$ remarks = join ( "\n" , @ briefsplit ) . dewikify ( $ wikitype , $ remarks ) ;
}
if ( defined $ brief ) {
$ str . = "\n" if $ addblank ; $ addblank = 1 ;
$ str . = wordwrap ( $ brief ) . "\n" ;
}
if ( defined $ remarks ) {
$ str . = "\n" if $ addblank ; $ addblank = 1 ;
$ str . = wordwrap ( $ remarks ) . "\n" ;
}
2021-10-24 02:37:47 +08:00
if ( defined $ deprecated ) {
# !!! FIXME: lots of code duplication in all of these.
$ str . = "\n" if $ addblank ; $ addblank = 1 ;
my $ v = dewikify ( $ wikitype , $ deprecated ) ;
my $ whitespacelen = length ( "\\deprecated" ) + 1 ;
my $ whitespace = ' ' x $ whitespacelen ;
$ v = wordwrap ( $ v , - $ whitespacelen ) ;
my @ desclines = split /\n/ , $ v ;
my $ firstline = shift @ desclines ;
$ str . = "\\deprecated $firstline\n" ;
foreach ( @ desclines ) {
$ str . = "${whitespace}$_\n" ;
}
}
2021-03-17 01:46:03 +08:00
if ( defined $ params ) {
$ str . = "\n" if $ addblank ; $ addblank = ( defined $ returns ) ? 0 : 1 ;
my @ lines = split /\n/ , dewikify ( $ wikitype , $ params ) ;
if ( $ wikitype eq 'mediawiki' ) {
die ( "Unexpected data parsing MediaWiki table" ) if ( shift @ lines ne '{|' ) ; # Dump the '{|' start
while ( scalar ( @ lines ) >= 3 ) {
2024-06-13 03:49:59 +08:00
my $ c_datatype = shift @ lines ;
2021-03-17 01:46:03 +08:00
my $ name = shift @ lines ;
my $ desc = shift @ lines ;
2024-06-13 03:49:59 +08:00
my $ terminator ; # the '|-' or '|}' line.
if ( ( $ desc eq '|-' ) or ( $ desc eq '|}' ) or ( not $ desc =~ /\A\|/ ) ) { # we seem to be out of cells, which means there was no datatype column on this one.
$ terminator = $ desc ;
$ desc = $ name ;
$ name = $ c_datatype ;
$ c_datatype = '' ;
} else {
$ terminator = shift @ lines ;
}
2021-03-17 01:46:03 +08:00
last if ( $ terminator ne '|-' ) and ( $ terminator ne '|}' ) ; # we seem to have run out of table.
$ name =~ s/\A\|\s*// ;
$ name =~ s/\A\*\*(.*?)\*\*/$1/ ;
$ name =~ s/\A\'\'\'(.*?)\'\'\'/$1/ ;
$ desc =~ s/\A\|\s*// ;
2024-06-13 03:49:59 +08:00
#print STDERR "SYM: $sym CDATATYPE: $c_datatype NAME: $name DESC: $desc TERM: $terminator\n";
2021-03-17 01:46:03 +08:00
my $ whitespacelen = length ( $ name ) + 8 ;
my $ whitespace = ' ' x $ whitespacelen ;
$ desc = wordwrap ( $ desc , - $ whitespacelen ) ;
my @ desclines = split /\n/ , $ desc ;
my $ firstline = shift @ desclines ;
2024-04-09 10:34:11 +08:00
$ str . = "$paramstr $name $firstline\n" ;
2021-03-17 01:46:03 +08:00
foreach ( @ desclines ) {
$ str . = "${whitespace}$_\n" ;
}
}
2023-02-24 08:53:53 +08:00
} elsif ( $ wikitype eq 'md' ) {
my $ l ;
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
die ( "Unexpected data parsing Markdown table" ) if ( not $ l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/ ) ;
2023-02-24 08:53:53 +08:00
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
die ( "Unexpected data parsing Markdown table" ) if ( not $ l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/ ) ;
2023-02-24 08:53:53 +08:00
while ( scalar ( @ lines ) >= 1 ) {
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
my $ name ;
my $ desc ;
if ( $ l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/ ) {
# c datatype is $1, but we don't care about it here.
$ name = $ 2 ;
$ desc = $ 3 ;
} elsif ( $ l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/ ) {
$ name = $ 1 ;
$ desc = $ 2 ;
2023-02-24 08:53:53 +08:00
} else {
last ; # we seem to have run out of table.
}
2024-06-13 03:49:59 +08:00
$ name =~ s/\A\*\*(.*?)\*\*/$1/ ;
$ name =~ s/\A\'\'\'(.*?)\'\'\'/$1/ ;
#print STDERR "SYM: $sym NAME: $name DESC: $desc\n";
my $ whitespacelen = length ( $ name ) + 8 ;
my $ whitespace = ' ' x $ whitespacelen ;
$ desc = wordwrap ( $ desc , - $ whitespacelen ) ;
my @ desclines = split /\n/ , $ desc ;
my $ firstline = shift @ desclines ;
$ str . = "$paramstr $name $firstline\n" ;
foreach ( @ desclines ) {
$ str . = "${whitespace}$_\n" ;
}
2023-02-24 08:53:53 +08:00
}
2021-03-17 01:46:03 +08:00
} else {
die ( "write me" ) ;
}
}
if ( defined $ returns ) {
$ str . = "\n" if $ addblank ; $ addblank = 1 ;
my $ r = dewikify ( $ wikitype , $ returns ) ;
2024-06-13 03:49:59 +08:00
$ r =~ s/\A\(.*?\)\s*// ; # Chop datatype in parentheses off the front.
2021-03-17 01:46:03 +08:00
my $ retstr = "\\returns" ;
2024-06-13 03:49:59 +08:00
if ( $ r =~ s/\AReturn(s?)\s+// ) {
2021-03-17 01:46:03 +08:00
$ retstr = "\\return$1" ;
}
my $ whitespacelen = length ( $ retstr ) + 1 ;
my $ whitespace = ' ' x $ whitespacelen ;
$ r = wordwrap ( $ r , - $ whitespacelen ) ;
my @ desclines = split /\n/ , $ r ;
my $ firstline = shift @ desclines ;
$ str . = "$retstr $firstline\n" ;
foreach ( @ desclines ) {
$ str . = "${whitespace}$_\n" ;
}
}
2023-01-25 11:13:25 +08:00
if ( defined $ threadsafety ) {
# !!! FIXME: lots of code duplication in all of these.
$ str . = "\n" if $ addblank ; $ addblank = 1 ;
my $ v = dewikify ( $ wikitype , $ threadsafety ) ;
my $ whitespacelen = length ( "\\threadsafety" ) + 1 ;
my $ whitespace = ' ' x $ whitespacelen ;
$ v = wordwrap ( $ v , - $ whitespacelen ) ;
my @ desclines = split /\n/ , $ v ;
my $ firstline = shift @ desclines ;
$ str . = "\\threadsafety $firstline\n" ;
foreach ( @ desclines ) {
$ str . = "${whitespace}$_\n" ;
}
}
2021-03-17 01:46:03 +08:00
if ( defined $ version ) {
# !!! FIXME: lots of code duplication in all of these.
$ str . = "\n" if $ addblank ; $ addblank = 1 ;
my $ v = dewikify ( $ wikitype , $ version ) ;
my $ whitespacelen = length ( "\\since" ) + 1 ;
my $ whitespace = ' ' x $ whitespacelen ;
$ v = wordwrap ( $ v , - $ whitespacelen ) ;
my @ desclines = split /\n/ , $ v ;
my $ firstline = shift @ desclines ;
$ str . = "\\since $firstline\n" ;
foreach ( @ desclines ) {
$ str . = "${whitespace}$_\n" ;
}
}
if ( defined $ related ) {
# !!! FIXME: lots of code duplication in all of these.
$ str . = "\n" if $ addblank ; $ addblank = 1 ;
my $ v = dewikify ( $ wikitype , $ related ) ;
my @ desclines = split /\n/ , $ v ;
foreach ( @ desclines ) {
2021-07-14 21:36:07 +08:00
s/\(\)\Z// ; # Convert "SDL_Func()" to "SDL_Func"
2022-06-19 12:15:03 +08:00
s/\[\[(.*?)\]\]/$1/ ; # in case some wikilinks remain.
2023-02-25 00:45:43 +08:00
s/\[(.*?)\]\(.*?\)/$1/ ; # in case some wikilinks remain.
2022-06-16 11:25:36 +08:00
s/\A\/*// ;
2024-05-08 02:23:02 +08:00
s/\A\s*[\:\*\-]\s*// ;
s/\A\s+// ;
s/\s+\Z// ;
2021-03-17 01:46:03 +08:00
$ str . = "\\sa $_\n" ;
}
}
2024-04-06 14:10:42 +08:00
my $ header = $ headersymslocation { $ sym } ;
2021-10-09 02:39:28 +08:00
my $ contentsref = $ headers { $ header } ;
2024-04-06 14:10:42 +08:00
my $ chunk = $ headersymschunk { $ sym } ;
2021-10-09 02:39:28 +08:00
2021-03-17 01:46:03 +08:00
my @ lines = split /\n/ , $ str ;
2021-10-09 02:39:28 +08:00
my $ addnewline = ( ( $ chunk > 0 ) && ( $$ contentsref [ $ chunk - 1 ] ne '' ) ) ? "\n" : '' ;
my $ output = "$addnewline/**\n" ;
2021-03-17 01:46:03 +08:00
foreach ( @ lines ) {
chomp ;
s/\s*\Z// ;
if ( $ _ eq '' ) {
$ output . = " *\n" ;
} else {
$ output . = " * $_\n" ;
}
}
$ output . = " */" ;
2024-05-16 22:44:37 +08:00
#print("$sym:\n[$output]\n\n");
2021-03-17 01:46:03 +08:00
$$ contentsref [ $ chunk ] = $ output ;
2024-04-06 14:10:42 +08:00
#$$contentsref[$chunk+1] = $headerdecls{$sym};
2021-03-17 01:46:03 +08:00
$ changed_headers { $ header } = 1 ;
}
foreach ( keys % changed_headers ) {
2021-10-09 02:39:28 +08:00
my $ header = $ _ ;
# this is kinda inefficient, but oh well.
my @ removelines = ( ) ;
2024-04-06 14:10:42 +08:00
foreach ( keys % headersymslocation ) {
my $ sym = $ _ ;
next if $ headersymshasdoxygen { $ sym } ;
next if $ headersymslocation { $ sym } ne $ header ;
2021-10-09 02:39:28 +08:00
# the index of the blank line we put before the function declaration in case we needed to replace it with new content from the wiki.
2024-04-06 14:10:42 +08:00
push @ removelines , $ headersymschunk { $ sym } ;
2021-10-09 02:39:28 +08:00
}
my $ contentsref = $ headers { $ header } ;
foreach ( @ removelines ) {
delete $$ contentsref [ $ _ ] ; # delete DOES NOT RENUMBER existing elements!
}
my $ path = "$incpath/$header.tmp" ;
2021-03-17 01:46:03 +08:00
open ( FH , '>' , $ path ) or die ( "Can't open '$path': $!\n" ) ;
foreach ( @$ contentsref ) {
2021-10-09 02:39:28 +08:00
print FH "$_\n" if defined $ _ ;
2021-03-17 01:46:03 +08:00
}
close ( FH ) ;
2021-10-09 02:39:28 +08:00
rename ( $ path , "$incpath/$header" ) or die ( "Can't rename '$path' to '$incpath/$header': $!\n" ) ;
2021-03-17 01:46:03 +08:00
}
2023-03-01 00:37:46 +08:00
if ( defined $ readmepath ) {
if ( - d $ wikireadmepath ) {
mkdir ( $ readmepath ) ; # just in case
opendir ( DH , $ wikireadmepath ) or die ( "Can't opendir '$wikireadmepath': $!\n" ) ;
while ( readdir ( DH ) ) {
my $ dent = $ _ ;
if ( $ dent =~ /\A(.*?)\.md\Z/ ) { # we only bridge Markdown files here.
2023-03-01 00:55:19 +08:00
next if $ 1 eq 'FrontPage' ;
2023-11-30 23:10:04 +08:00
filecopy ( "$wikireadmepath/$dent" , "$readmepath/README-$dent" , "\n" ) ;
2023-03-01 00:37:46 +08:00
}
}
closedir ( DH ) ;
}
}
2024-05-16 22:44:37 +08:00
2021-03-17 01:46:03 +08:00
} elsif ( $ copy_direction == - 1 ) { # --copy-to-wiki
2023-02-24 08:53:53 +08:00
if ( defined $ changeformat ) {
$ dewikify_mode = $ changeformat ;
$ wordwrap_mode = $ changeformat ;
}
2024-04-06 14:10:42 +08:00
foreach ( keys % headersyms ) {
my $ sym = $ _ ;
next if not $ headersymshasdoxygen { $ sym } ;
2024-05-16 22:44:37 +08:00
next if $ sym =~ /\A\[category documentation\]/ ; # not real symbols, we handle this elsewhere.
2024-04-06 14:10:42 +08:00
my $ symtype = $ headersymstype { $ sym } ;
my $ origwikitype = defined $ wikitypes { $ sym } ? $ wikitypes { $ sym } : 'md' ; # default to MarkDown for new stuff.
2023-02-24 08:53:53 +08:00
my $ wikitype = ( defined $ changeformat ) ? $ changeformat : $ origwikitype ;
2024-04-09 10:32:36 +08:00
die ( "Unexpected wikitype '$wikitype'" ) if ( ( $ wikitype ne 'mediawiki' ) and ( $ wikitype ne 'md' ) and ( $ wikitype ne 'manpage' ) ) ;
2021-03-17 01:46:03 +08:00
2024-04-06 14:10:42 +08:00
#print("$sym\n"); next;
2021-03-24 22:45:27 +08:00
$ wordwrap_mode = $ wikitype ;
2024-04-06 14:10:42 +08:00
my $ raw = $ headersyms { $ sym } ; # raw doxygen text with comment characters stripped from start/end and start of each line.
2021-10-09 02:39:28 +08:00
next if not defined $ raw ;
2021-03-17 01:46:03 +08:00
$ raw =~ s/\A\s*\\brief\s+// ; # Technically we don't need \brief (please turn on JAVADOC_AUTOBRIEF if you use Doxygen), so just in case one is present, strip it.
my @ doxygenlines = split /\n/ , $ raw ;
my $ brief = '' ;
while ( @ doxygenlines ) {
last if $ doxygenlines [ 0 ] =~ /\A\\/ ; # some sort of doxygen command, assume we're past the general remarks.
last if $ doxygenlines [ 0 ] =~ /\A\s*\Z/ ; # blank line? End of paragraph, done.
my $ l = shift @ doxygenlines ;
chomp ( $ l ) ;
$ l =~ s/\A\s*// ;
$ l =~ s/\s*\Z// ;
$ brief . = "$l " ;
}
2024-04-07 11:25:02 +08:00
$ brief =~ s/\s+\Z// ;
2021-03-17 01:46:03 +08:00
$ brief =~ s/\A(.*?\.) /$1\n\n/ ; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
my @ briefsplit = split /\n/ , $ brief ;
2024-04-06 14:10:42 +08:00
next if not defined $ briefsplit [ 0 ] ; # No brief text? Probably a bogus Doxygen comment, skip it.
2021-03-24 22:45:27 +08:00
$ brief = wikify ( $ wikitype , shift @ briefsplit ) . "\n" ;
2021-03-17 01:46:03 +08:00
@ doxygenlines = ( @ briefsplit , @ doxygenlines ) ;
my $ remarks = '' ;
while ( @ doxygenlines ) {
last if $ doxygenlines [ 0 ] =~ /\A\\/ ; # some sort of doxygen command, assume we're past the general remarks.
my $ l = shift @ doxygenlines ;
2024-04-09 10:34:11 +08:00
$ remarks . = "$l\n" ;
2021-03-17 01:46:03 +08:00
}
2021-07-14 20:11:18 +08:00
#print("REMARKS:\n\n $remarks\n\n");
2021-03-17 01:46:03 +08:00
$ remarks = wordwrap ( wikify ( $ wikitype , $ remarks ) ) ;
$ remarks =~ s/\A\s*// ;
$ remarks =~ s/\s*\Z// ;
2024-04-06 14:10:42 +08:00
my $ decl = $ headerdecls { $ sym } ;
2021-03-17 01:46:03 +08:00
my $ syntax = '' ;
if ( $ wikitype eq 'mediawiki' ) {
$ syntax = "<syntaxhighlight lang='c'>\n$decl</syntaxhighlight>\n" ;
} elsif ( $ wikitype eq 'md' ) {
2024-06-12 00:37:41 +08:00
$ decl =~ s/\n+\Z// ;
2021-03-17 01:46:03 +08:00
$ syntax = "```c\n$decl\n```\n" ;
2024-04-09 10:32:36 +08:00
} else { die ( "Expected wikitype '$wikitype'" ) ; }
2021-03-17 01:46:03 +08:00
my % sections = ( ) ;
$ sections { '[Brief]' } = $ brief ; # include this section even if blank so we get a title line.
$ sections { 'Remarks' } = "$remarks\n" if $ remarks ne '' ;
$ sections { 'Syntax' } = $ syntax ;
2024-06-13 03:49:59 +08:00
my % params = ( ) ; # have to parse these and build up the wiki tables after, since Markdown needs to know the length of the largest string. :/
my @ paramsorder = ( ) ;
my $ fnsigparams = $ headersymsparaminfo { $ sym } ;
2024-06-14 04:45:59 +08:00
my $ has_returns = 0 ;
2024-10-22 22:47:51 +08:00
my $ has_threadsafety = 0 ;
2021-03-17 01:46:03 +08:00
while ( @ doxygenlines ) {
my $ l = shift @ doxygenlines ;
2024-04-09 10:34:11 +08:00
# We allow param/field/value interchangeably, even if it doesn't make sense. The next --copy-to-headers will correct it anyhow.
if ( $ l =~ /\A\\(param|field|value)\s+(.*?)\s+(.*)\Z/ ) {
my $ arg = $ 2 ;
my $ desc = $ 3 ;
2021-03-17 01:46:03 +08:00
while ( @ doxygenlines ) {
my $ subline = $ doxygenlines [ 0 ] ;
$ subline =~ s/\A\s*// ;
last if $ subline =~ /\A\\/ ; # some sort of doxygen command, assume we're past this thing.
shift @ doxygenlines ; # dump this line from the array; we're using it.
2021-07-14 23:58:57 +08:00
if ( $ subline eq '' ) { # empty line, make sure it keeps the newline char.
$ desc . = "\n" ;
} else {
$ desc . = " $subline" ;
}
2021-03-17 01:46:03 +08:00
}
2021-07-14 23:58:57 +08:00
$ desc =~ s/[\s\n]+\Z//ms ;
2024-06-14 13:57:48 +08:00
if ( 0 ) { # !!! FIXME: disabled because it's not currently suitable for general use, but for manually inspecting the output, it can be useful.
if ( ( $ desc =~ /\A[A-Z]/ ) && ( not $ desc =~ /\ASDL_/ ) ) {
print STDERR "WARNING: $sym\'s '\\param $arg' text starts with a capital letter: '$desc'. Fixing.\n" ;
$ desc = lcfirst ( $ desc ) ;
}
}
if ( not $ desc =~ /[\.\!]\Z/ ) {
print STDERR "WARNING: $sym\'s '\\param $arg' text doesn't end with punctuation: '$desc'. Fixing.\n" ;
$ desc . = '.' ;
}
2024-06-13 03:49:59 +08:00
# Validate this param.
if ( defined ( $ params { $ arg } ) ) {
print STDERR "WARNING: Symbol '$sym' has multiple '\\param $arg' declarations! Only keeping the first one!\n" ;
} elsif ( defined $ fnsigparams ) {
my $ found = 0 ;
for ( my $ i = 0 ; $ i < scalar ( @$ fnsigparams ) ; $ i += 2 ) {
$ found = 1 , last if ( @$ fnsigparams [ $ i ] eq $ arg ) ;
}
if ( ! $ found ) {
print STDERR "WARNING: Symbol '$sym' has a '\\param $arg' for a param that doesn't exist. It will be removed!\n" ;
}
}
2021-03-17 01:46:03 +08:00
# We need to know the length of the longest string to make Markdown tables, so we just store these off until everything is parsed.
2024-06-13 03:49:59 +08:00
$ params { $ arg } = $ desc ;
push @ paramsorder , $ arg ;
2021-03-17 01:46:03 +08:00
} elsif ( $ l =~ /\A\\r(eturns?)\s+(.*)\Z/ ) {
2024-06-14 04:45:59 +08:00
$ has_returns = 1 ;
2024-06-13 03:49:59 +08:00
# !!! FIXME: complain if this isn't a function or macro.
2021-03-17 01:46:03 +08:00
my $ retstr = "R$1" ; # "Return" or "Returns"
my $ desc = $ 2 ;
2024-06-14 06:10:28 +08:00
2021-03-17 01:46:03 +08:00
while ( @ doxygenlines ) {
my $ subline = $ doxygenlines [ 0 ] ;
$ subline =~ s/\A\s*// ;
last if $ subline =~ /\A\\/ ; # some sort of doxygen command, assume we're past this thing.
shift @ doxygenlines ; # dump this line from the array; we're using it.
2021-07-14 23:58:57 +08:00
if ( $ subline eq '' ) { # empty line, make sure it keeps the newline char.
$ desc . = "\n" ;
} else {
$ desc . = " $subline" ;
}
2021-03-17 01:46:03 +08:00
}
2021-07-14 23:58:57 +08:00
$ desc =~ s/[\s\n]+\Z//ms ;
2024-06-13 03:49:59 +08:00
2024-06-14 13:57:48 +08:00
if ( 0 ) { # !!! FIXME: disabled because it's not currently suitable for general use, but for manually inspecting the output, it can be useful.
if ( ( $ desc =~ /\A[A-Z]/ ) && ( not $ desc =~ /\ASDL_/ ) ) {
print STDERR "WARNING: $sym\'s '\\returns' text starts with a capital letter: '$desc'. Fixing.\n" ;
$ desc = lcfirst ( $ desc ) ;
}
}
if ( not $ desc =~ /[\.\!]\Z/ ) {
print STDERR "WARNING: $sym\'s '\\returns' text doesn't end with punctuation: '$desc'. Fixing.\n" ;
$ desc . = '.' ;
}
2024-06-13 03:49:59 +08:00
# Make sure the \returns info is valid.
my $ rettype = $ headersymsrettype { $ sym } ;
die ( "Don't have a rettype for '$sym' for some reason!" ) if ( ( $ symtype == 1 ) && ( not defined ( $ rettype ) ) ) ;
if ( defined ( $ sections { 'Return Value' } ) ) {
print STDERR "WARNING: Symbol '$sym' has multiple '\\return' declarations! Only keeping the first one!\n" ;
} elsif ( ( $ symtype != 1 ) && ( $ symtype != 2 ) && ( $ symtype != 5 ) ) { # !!! FIXME: if 5, make sure it's a function pointer typedef!
print STDERR "WARNING: Symbol '$sym' has a '\\return' declaration but isn't a function or macro! Removing it!\n" ;
} elsif ( ( $ symtype == 1 ) && ( $ headersymsrettype { $ sym } eq 'void' ) ) {
print STDERR "WARNING: Function '$sym' has a '\\returns' declaration but function returns void! Removing it!\n" ;
} else {
my $ rettypestr = defined ( $ rettype ) ? ( '(' . wikify ( $ wikitype , $ rettype ) . ') ' ) : '' ;
$ sections { 'Return Value' } = wordwrap ( "$rettypestr$retstr " . wikify ( $ wikitype , $ desc ) ) . "\n" ;
}
2021-10-24 02:37:47 +08:00
} elsif ( $ l =~ /\A\\deprecated\s+(.*)\Z/ ) {
my $ desc = $ 1 ;
while ( @ doxygenlines ) {
my $ subline = $ doxygenlines [ 0 ] ;
$ subline =~ s/\A\s*// ;
last if $ subline =~ /\A\\/ ; # some sort of doxygen command, assume we're past this thing.
shift @ doxygenlines ; # dump this line from the array; we're using it.
if ( $ subline eq '' ) { # empty line, make sure it keeps the newline char.
$ desc . = "\n" ;
} else {
$ desc . = " $subline" ;
}
}
$ desc =~ s/[\s\n]+\Z//ms ;
$ sections { 'Deprecated' } = wordwrap ( wikify ( $ wikitype , $ desc ) ) . "\n" ;
2021-03-17 01:46:03 +08:00
} elsif ( $ l =~ /\A\\since\s+(.*)\Z/ ) {
my $ desc = $ 1 ;
while ( @ doxygenlines ) {
my $ subline = $ doxygenlines [ 0 ] ;
$ subline =~ s/\A\s*// ;
last if $ subline =~ /\A\\/ ; # some sort of doxygen command, assume we're past this thing.
shift @ doxygenlines ; # dump this line from the array; we're using it.
2021-07-14 23:58:57 +08:00
if ( $ subline eq '' ) { # empty line, make sure it keeps the newline char.
$ desc . = "\n" ;
} else {
$ desc . = " $subline" ;
}
2021-03-17 01:46:03 +08:00
}
2021-07-14 23:58:57 +08:00
$ desc =~ s/[\s\n]+\Z//ms ;
$ sections { 'Version' } = wordwrap ( wikify ( $ wikitype , $ desc ) ) . "\n" ;
2023-01-25 11:13:25 +08:00
} elsif ( $ l =~ /\A\\threadsafety\s+(.*)\Z/ ) {
my $ desc = $ 1 ;
while ( @ doxygenlines ) {
my $ subline = $ doxygenlines [ 0 ] ;
$ subline =~ s/\A\s*// ;
last if $ subline =~ /\A\\/ ; # some sort of doxygen command, assume we're past this thing.
shift @ doxygenlines ; # dump this line from the array; we're using it.
if ( $ subline eq '' ) { # empty line, make sure it keeps the newline char.
$ desc . = "\n" ;
} else {
$ desc . = " $subline" ;
}
}
$ desc =~ s/[\s\n]+\Z//ms ;
$ sections { 'Thread Safety' } = wordwrap ( wikify ( $ wikitype , $ desc ) ) . "\n" ;
2024-10-22 22:47:51 +08:00
$ has_threadsafety = 1 ;
2021-03-17 01:46:03 +08:00
} elsif ( $ l =~ /\A\\sa\s+(.*)\Z/ ) {
my $ sa = $ 1 ;
2021-07-14 21:36:07 +08:00
$ sa =~ s/\(\)\Z// ; # Convert "SDL_Func()" to "SDL_Func"
2024-04-07 11:25:37 +08:00
$ sections { 'See Also' } = '' if not defined $ sections { 'See Also' } ;
2021-03-17 01:46:03 +08:00
if ( $ wikitype eq 'mediawiki' ) {
2024-04-07 11:25:37 +08:00
$ sections { 'See Also' } . = ":[[$sa]]\n" ;
2021-03-17 01:46:03 +08:00
} elsif ( $ wikitype eq 'md' ) {
2024-05-05 13:21:02 +08:00
$ sections { 'See Also' } . = "- [$sa]($sa)\n" ;
2024-04-09 10:32:36 +08:00
} else { die ( "Expected wikitype '$wikitype'" ) ; }
2021-03-17 01:46:03 +08:00
}
}
2024-06-14 04:45:59 +08:00
if ( ( $ symtype == 1 ) && ( $ headersymsrettype { $ sym } ne 'void' ) && ! $ has_returns ) {
print STDERR "WARNING: Function '$sym' has a non-void return type but no '\\returns' declaration\n" ;
}
2024-10-22 22:47:51 +08:00
# !!! FIXME: uncomment this when we're trying to clean this up in the headers.
#if (($symtype == 1) && !$has_threadsafety) {
# print STDERR "WARNING: Function '$sym' doesn't have a '\\threadsafety' declaration\n";
#}
2024-06-13 03:49:59 +08:00
# Make sure %params is in the same order as the actual function signature and add C datatypes...
my $ params_has_c_datatype = 0 ;
my @ final_params = ( ) ;
if ( ( $ symtype == 1 ) && ( defined ( $ headersymsparaminfo { $ sym } ) ) ) { # is a function and we have param info for it...
my $ fnsigparams = $ headersymsparaminfo { $ sym } ;
for ( my $ i = 0 ; $ i < scalar ( @$ fnsigparams ) ; $ i += 2 ) {
my $ paramname = @$ fnsigparams [ $ i ] ;
my $ paramdesc = $ params { $ paramname } ;
if ( defined ( $ paramdesc ) ) {
push @ final_params , $ paramname ; # name
push @ final_params , @$ fnsigparams [ $ i + 1 ] ; # C datatype
push @ final_params , $ paramdesc ; # description
$ params_has_c_datatype = 1 if ( defined ( @$ fnsigparams [ $ i + 1 ] ) ) ;
} else {
print STDERR "WARNING: Symbol '$sym' is missing a '\\param $paramname' declaration!\n" ;
}
}
} else {
foreach ( @ paramsorder ) {
my $ paramname = $ _ ;
my $ paramdesc = $ params { $ paramname } ;
if ( defined ( $ paramdesc ) ) {
push @ final_params , $ _ ;
push @ final_params , undef ;
push @ final_params , $ paramdesc ;
}
}
}
2024-04-01 00:56:00 +08:00
my $ hfiletext = $ wikiheaderfiletext ;
2024-04-06 14:10:42 +08:00
$ hfiletext =~ s/\%fname\%/$headersymslocation{$sym}/g ;
2024-04-01 00:56:00 +08:00
$ sections { 'Header File' } = "$hfiletext\n" ;
2021-03-24 22:45:27 +08:00
# Make sure this ends with a double-newline.
2024-04-07 11:25:37 +08:00
$ sections { 'See Also' } . = "\n" if defined $ sections { 'See Also' } ;
2021-03-24 22:45:27 +08:00
2024-04-12 01:34:29 +08:00
if ( 0 ) { # !!! FIXME: this was a useful hack, but this needs to be generalized if we're going to do this always.
# Plug in a \since section if one wasn't listed.
if ( not defined $ sections { 'Version' } ) {
my $ symtypename ;
if ( $ symtype == 1 ) {
$ symtypename = 'function' ;
} elsif ( $ symtype == 2 ) {
$ symtypename = 'macro' ;
} elsif ( $ symtype == 3 ) {
$ symtypename = 'struct' ;
} elsif ( $ symtype == 4 ) {
$ symtypename = 'enum' ;
} elsif ( $ symtype == 5 ) {
$ symtypename = 'datatype' ;
} else {
die ( "Unexpected symbol type $symtype!" ) ;
}
my $ str = "This $symtypename is available since SDL 3.0.0." ;
$ sections { 'Version' } = wordwrap ( wikify ( $ wikitype , $ str ) ) . "\n" ;
}
}
2021-03-17 01:46:03 +08:00
# We can build the wiki table now that we have all the data.
2024-06-13 03:49:59 +08:00
if ( scalar ( @ final_params ) > 0 ) {
2021-03-17 01:46:03 +08:00
my $ str = '' ;
if ( $ wikitype eq 'mediawiki' ) {
2024-06-13 03:49:59 +08:00
while ( scalar ( @ final_params ) > 0 ) {
my $ arg = shift @ final_params ;
my $ c_datatype = shift @ final_params ;
my $ desc = wikify ( $ wikitype , shift @ final_params ) ;
$ c_datatype = '' if not defined $ c_datatype ;
2021-03-17 01:46:03 +08:00
$ str . = ( $ str eq '' ) ? "{|\n" : "|-\n" ;
2024-06-13 03:49:59 +08:00
$ str . = "|$c_datatype\n" if $ params_has_c_datatype ;
2021-03-17 01:46:03 +08:00
$ str . = "|'''$arg'''\n" ;
$ str . = "|$desc\n" ;
}
$ str . = "|}\n" ;
} elsif ( $ wikitype eq 'md' ) {
my $ longest_arg = 0 ;
2024-06-13 03:49:59 +08:00
my $ longest_c_datatype = 0 ;
2021-03-17 01:46:03 +08:00
my $ longest_desc = 0 ;
my $ which = 0 ;
2024-06-13 03:49:59 +08:00
foreach ( @ final_params ) {
2021-03-17 01:46:03 +08:00
if ( $ which == 0 ) {
2024-06-13 03:49:59 +08:00
my $ len = length ( $ _ ) ;
2021-03-17 01:46:03 +08:00
$ longest_arg = $ len if ( $ len > $ longest_arg ) ;
$ which = 1 ;
2024-06-13 03:49:59 +08:00
} elsif ( $ which == 1 ) {
if ( defined ( $ _ ) ) {
my $ len = length ( wikify ( $ wikitype , $ _ ) ) ;
$ longest_c_datatype = $ len if ( $ len > $ longest_c_datatype ) ;
}
$ which = 2 ;
2021-03-17 01:46:03 +08:00
} else {
2021-03-24 22:45:27 +08:00
my $ len = length ( wikify ( $ wikitype , $ _ ) ) ;
2021-03-17 01:46:03 +08:00
$ longest_desc = $ len if ( $ len > $ longest_desc ) ;
$ which = 0 ;
}
}
# Markdown tables are sort of obnoxious.
2024-06-13 03:49:59 +08:00
my $ c_datatype_cell ;
$ c_datatype_cell = ( $ longest_c_datatype > 0 ) ? ( '| ' . ( ' ' x ( $ longest_c_datatype ) ) . ' ' ) : '' ;
$ str . = $ c_datatype_cell . '| ' . ( ' ' x ( $ longest_arg + 4 ) ) . ' | ' . ( ' ' x $ longest_desc ) . " |\n" ;
$ c_datatype_cell = ( $ longest_c_datatype > 0 ) ? ( '| ' . ( '-' x ( $ longest_c_datatype ) ) . ' ' ) : '' ;
$ str . = $ c_datatype_cell . '| ' . ( '-' x ( $ longest_arg + 4 ) ) . ' | ' . ( '-' x $ longest_desc ) . " |\n" ;
while ( @ final_params ) {
my $ arg = shift @ final_params ;
my $ c_datatype = shift @ final_params ;
$ c_datatype_cell = '' ;
if ( $ params_has_c_datatype ) {
$ c_datatype = defined ( $ c_datatype ) ? wikify ( $ wikitype , $ c_datatype ) : '' ;
$ c_datatype_cell = ( $ longest_c_datatype > 0 ) ? ( "| $c_datatype " . ( ' ' x ( $ longest_c_datatype - length ( $ c_datatype ) ) ) ) : '' ;
}
my $ desc = wikify ( $ wikitype , shift @ final_params ) ;
$ str . = $ c_datatype_cell . "| **$arg** " . ( ' ' x ( $ longest_arg - length ( $ arg ) ) ) . "| $desc" . ( ' ' x ( $ longest_desc - length ( $ desc ) ) ) . " |\n" ;
2021-03-17 01:46:03 +08:00
}
} else {
2024-04-09 10:32:36 +08:00
die ( "Unexpected wikitype!" ) ; # should have checked this elsewhere.
2021-03-17 01:46:03 +08:00
}
$ sections { 'Function Parameters' } = $ str ;
}
2024-05-16 22:44:37 +08:00
my $ path = "$wikipath/$sym.${wikitype}.tmp" ;
2021-03-17 01:46:03 +08:00
open ( FH , '>' , $ path ) or die ( "Can't open '$path': $!\n" ) ;
2024-04-06 14:10:42 +08:00
my $ sectionsref = $ wikisyms { $ sym } ;
2021-03-17 01:46:03 +08:00
foreach ( @ standard_wiki_sections ) {
# drop sections we either replaced or removed from the original wiki's contents.
2021-03-24 22:46:05 +08:00
if ( not defined $ only_wiki_sections { $ _ } ) {
delete ( $$ sectionsref { $ _ } ) ;
}
2021-03-17 01:46:03 +08:00
}
2024-04-06 14:10:42 +08:00
my $ wikisectionorderref = $ wikisectionorder { $ sym } ;
2021-03-17 01:46:03 +08:00
2021-09-01 12:19:54 +08:00
# Make sure there's a footer in the wiki that puts this function in CategoryAPI...
if ( not $$ sectionsref { '[footer]' } ) {
$$ sectionsref { '[footer]' } = '' ;
push @$ wikisectionorderref , '[footer]' ;
}
2023-02-24 08:53:53 +08:00
# If changing format, convert things that otherwise are passed through unmolested.
if ( defined $ changeformat ) {
if ( ( $ dewikify_mode eq 'md' ) and ( $ origwikitype eq 'mediawiki' ) ) {
$$ sectionsref { '[footer]' } =~ s/\[\[(Category[a-zA-Z0-9_]+)\]\]/[$1]($1)/g ;
} elsif ( ( $ dewikify_mode eq 'mediawiki' ) and ( $ origwikitype eq 'md' ) ) {
$$ sectionsref { '[footer]' } =~ s/\[(Category[a-zA-Z0-9_]+)\]\(.*?\)/[[$1]]/g ;
}
foreach ( keys % only_wiki_sections ) {
my $ sect = $ _ ;
if ( defined $$ sectionsref { $ sect } ) {
$$ sectionsref { $ sect } = wikify ( $ wikitype , dewikify ( $ origwikitype , $$ sectionsref { $ sect } ) ) ;
}
}
}
2024-05-16 22:44:37 +08:00
if ( $ symtype != - 1 ) { # Don't do these in category documentation block
my $ footer = $$ sectionsref { '[footer]' } ;
my $ symtypename ;
if ( $ symtype == 1 ) {
$ symtypename = 'Function' ;
} elsif ( $ symtype == 2 ) {
$ symtypename = 'Macro' ;
} elsif ( $ symtype == 3 ) {
$ symtypename = 'Struct' ;
} elsif ( $ symtype == 4 ) {
$ symtypename = 'Enum' ;
} elsif ( $ symtype == 5 ) {
$ symtypename = 'Datatype' ;
} else {
die ( "Unexpected symbol type $symtype!" ) ;
}
2021-09-01 12:19:54 +08:00
2024-05-16 22:44:37 +08:00
my $ symcategory = $ headersymscategory { $ sym } ;
2022-06-18 02:39:50 +08:00
if ( $ wikitype eq 'mediawiki' ) {
2024-05-16 22:44:37 +08:00
$ footer =~ s/\[\[CategoryAPI\]\],?\s*//g ;
$ footer =~ s/\[\[CategoryAPI${symtypename}\]\],?\s*//g ;
$ footer =~ s/\[\[Category${symcategory}\]\],?\s*//g if defined $ symcategory ;
$ footer = "[[CategoryAPI]], [[CategoryAPI$symtypename]]" . ( defined $ symcategory ? ", [[Category$symcategory]]" : '' ) . ( ( $ footer eq '' ) ? "\n" : ", $footer" ) ;
2022-06-18 02:39:50 +08:00
} elsif ( $ wikitype eq 'md' ) {
2024-05-16 22:44:37 +08:00
$ footer =~ s/\[CategoryAPI\]\(CategoryAPI\),?\s*//g ;
$ footer =~ s/\[CategoryAPI${symtypename}\]\(CategoryAPI${symtypename}\),?\s*//g ;
$ footer =~ s/\[Category${symcategory}\]\(Category${symcategory}\),?\s*//g if defined $ symcategory ;
$ footer = "[CategoryAPI](CategoryAPI), [CategoryAPI$symtypename](CategoryAPI$symtypename)" . ( defined $ symcategory ? ", [Category$symcategory](Category$symcategory)" : '' ) . ( ( $ footer eq '' ) ? '' : ', ' ) . $ footer ;
2024-04-09 10:32:36 +08:00
} else { die ( "Unexpected wikitype '$wikitype'" ) ; }
2024-05-16 22:44:37 +08:00
$$ sectionsref { '[footer]' } = $ footer ;
if ( defined $ wikipreamble ) {
my $ wikified_preamble = wikify ( $ wikitype , $ wikipreamble ) ;
if ( $ wikitype eq 'mediawiki' ) {
print FH "====== $wikified_preamble ======\n" ;
} elsif ( $ wikitype eq 'md' ) {
print FH "###### $wikified_preamble\n" ;
} else { die ( "Unexpected wikitype '$wikitype'" ) ; }
}
2022-06-18 02:39:50 +08:00
}
2021-09-01 12:19:54 +08:00
my $ prevsectstr = '' ;
my @ ordered_sections = ( @ standard_wiki_sections , defined $ wikisectionorderref ? @$ wikisectionorderref : ( ) ) ; # this copies the arrays into one.
2021-03-17 01:46:03 +08:00
foreach ( @ ordered_sections ) {
my $ sect = $ _ ;
next if $ sect eq '[start]' ;
next if ( not defined $ sections { $ sect } and not defined $$ sectionsref { $ sect } ) ;
my $ section = defined $ sections { $ sect } ? $ sections { $ sect } : $$ sectionsref { $ sect } ;
2024-04-09 10:34:11 +08:00
2021-03-17 01:46:03 +08:00
if ( $ sect eq '[footer]' ) {
2021-09-01 12:19:54 +08:00
# Make sure previous section ends with two newlines.
if ( substr ( $ prevsectstr , - 1 ) ne "\n" ) {
print FH "\n\n" ;
} elsif ( substr ( $ prevsectstr , - 2 ) ne "\n\n" ) {
print FH "\n" ;
}
2021-03-17 01:46:03 +08:00
print FH "----\n" ; # It's the same in Markdown and MediaWiki.
} elsif ( $ sect eq '[Brief]' ) {
if ( $ wikitype eq 'mediawiki' ) {
2024-04-06 14:10:42 +08:00
print FH "= $sym =\n\n" ;
2021-03-17 01:46:03 +08:00
} elsif ( $ wikitype eq 'md' ) {
2024-04-06 14:10:42 +08:00
print FH "# $sym\n\n" ;
2024-04-09 10:32:36 +08:00
} else { die ( "Unexpected wikitype '$wikitype'" ) ; }
2021-03-17 01:46:03 +08:00
} else {
2024-04-09 10:34:11 +08:00
my $ sectname = $ sect ;
if ( $ sectname eq 'Function Parameters' ) { # We use this same table for different things depending on what we're documenting, so rename it now.
2024-04-11 12:36:10 +08:00
if ( ( $ symtype == 1 ) || ( $ symtype == 5 ) ) { # function (or typedef, in case it's a function pointer type).
2024-04-09 10:34:11 +08:00
} elsif ( $ symtype == 2 ) { # macro
$ sectname = 'Macro Parameters' ;
} elsif ( $ symtype == 3 ) { # struct/union
$ sectname = 'Fields' ;
} elsif ( $ symtype == 4 ) { # enum
$ sectname = 'Values' ;
} else {
die ( "Unexpected symtype $symtype" ) ;
}
}
2024-05-16 22:44:37 +08:00
if ( $ symtype != - 1 ) { # Not for category documentation block
if ( $ wikitype eq 'mediawiki' ) {
print FH "\n== $sectname ==\n\n" ;
} elsif ( $ wikitype eq 'md' ) {
print FH "\n## $sectname\n\n" ;
} else { die ( "Unexpected wikitype '$wikitype'" ) ; }
}
2021-03-17 01:46:03 +08:00
}
2021-09-01 12:19:54 +08:00
my $ sectstr = defined $ sections { $ sect } ? $ sections { $ sect } : $$ sectionsref { $ sect } ;
print FH $ sectstr ;
$ prevsectstr = $ sectstr ;
2021-03-17 01:46:03 +08:00
# make sure these don't show up twice.
delete ( $ sections { $ sect } ) ;
delete ( $$ sectionsref { $ sect } ) ;
}
print FH "\n\n" ;
close ( FH ) ;
2023-02-24 08:53:53 +08:00
if ( defined $ changeformat and ( $ origwikitype ne $ wikitype ) ) {
system ( "cd '$wikipath' ; git mv '$_.${origwikitype}' '$_.${wikitype}'" ) ;
unlink ( "$wikipath/$_.${origwikitype}" ) ;
}
2021-03-17 01:46:03 +08:00
rename ( $ path , "$wikipath/$_.${wikitype}" ) or die ( "Can't rename '$path' to '$wikipath/$_.${wikitype}': $!\n" ) ;
}
2022-01-07 04:37:05 +08:00
2024-05-01 10:47:02 +08:00
# Write out simple redirector pages if they don't already exist.
foreach ( keys % referenceonly ) {
my $ sym = $ _ ;
my $ refersto = $ referenceonly { $ sym } ;
my $ path = "$wikipath/$sym.md" ; # we only do Markdown for these.
next if ( - f $ path ) ; # don't overwrite if it already exists. Delete the file if you need a rebuild!
open ( FH , '>' , $ path ) or die ( "Can't open '$path': $!\n" ) ;
if ( defined $ wikipreamble ) {
my $ wikified_preamble = wikify ( 'md' , $ wikipreamble ) ;
print FH "###### $wikified_preamble\n" ;
}
2024-08-17 11:45:14 +08:00
my $ category = 'CategoryAPIMacro' ;
if ( $ headersymstype { $ refersto } == 4 ) {
$ category = 'CategoryAPIEnumerators' ; # NOT CategoryAPIEnum!
}
2024-05-01 10:47:02 +08:00
print FH "# $sym\n\nPlease refer to [$refersto]($refersto) for details.\n\n" ;
2024-05-14 22:19:34 +08:00
print FH "----\n" ;
2024-08-17 11:45:14 +08:00
print FH "[CategoryAPI](CategoryAPI), [$category]($category)\n\n" ;
2024-05-01 10:47:02 +08:00
close ( FH ) ;
}
2024-05-16 22:44:37 +08:00
# Write out Category pages...
foreach ( keys % headercategorydocs ) {
my $ cat = $ _ ;
my $ sym = $ headercategorydocs { $ cat } ; # fake symbol
my $ raw = $ headersyms { $ sym } ; # raw doxygen text with comment characters stripped from start/end and start of each line.
my $ wikitype = defined ( $ wikitypes { $ sym } ) ? $ wikitypes { $ sym } : 'md' ;
my $ path = "$wikipath/Category$cat.$wikitype" ;
$ raw = wordwrap ( wikify ( $ wikitype , $ raw ) ) ;
my $ tmppath = "$path.tmp" ;
open ( FH , '>' , $ tmppath ) or die ( "Can't open '$tmppath': $!\n" ) ;
print FH "$raw\n\n" ;
if ( ! - f $ path ) { # Doesn't exist at all? Write out a template file.
# If writing from scratch, it's always a Markdown file.
die ( "Unexpected wikitype '$wikitype'!" ) if $ wikitype ne 'md' ;
print FH << __EOF__
< ! - - END CATEGORY DOCUMENTATION - - >
## Functions
< ! - - DO NOT HAND - EDIT CATEGORY LISTS , THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN , BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS . EDIT THOSE INSTEAD . - - >
< ! - - BEGIN CATEGORY LIST: Category $ cat , CategoryAPIFunction - - >
< ! - - END CATEGORY LIST - - >
## Datatypes
< ! - - DO NOT HAND - EDIT CATEGORY LISTS , THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN , BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS . EDIT THOSE INSTEAD . - - >
< ! - - BEGIN CATEGORY LIST: Category $ cat , CategoryAPIDatatype - - >
< ! - - END CATEGORY LIST - - >
## Structs
< ! - - DO NOT HAND - EDIT CATEGORY LISTS , THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN , BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS . EDIT THOSE INSTEAD . - - >
< ! - - BEGIN CATEGORY LIST: Category $ cat , CategoryAPIStruct - - >
< ! - - END CATEGORY LIST - - >
## Enums
< ! - - DO NOT HAND - EDIT CATEGORY LISTS , THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN , BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS . EDIT THOSE INSTEAD . - - >
< ! - - BEGIN CATEGORY LIST: Category $ cat , CategoryAPIEnum - - >
< ! - - END CATEGORY LIST - - >
## Macros
< ! - - DO NOT HAND - EDIT CATEGORY LISTS , THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN , BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS . EDIT THOSE INSTEAD . - - >
< ! - - BEGIN CATEGORY LIST: Category $ cat , CategoryAPIMacro - - >
< ! - - END CATEGORY LIST - - >
- - - -
[ CategoryAPICategory ] ( CategoryAPICategory )
__EOF__
;
} else {
my $ endstr = $ wikisyms { $ sym } - > { '[footer]' } ;
if ( defined ( $ endstr ) ) {
print FH $ endstr ;
}
}
close ( FH ) ;
rename ( $ tmppath , $ path ) or die ( "Can't rename '$tmppath' to '$path': $!\n" ) ;
}
# Write out READMEs...
2023-03-01 00:37:46 +08:00
if ( defined $ readmepath ) {
if ( - d $ readmepath ) {
mkdir ( $ wikireadmepath ) ; # just in case
opendir ( DH , $ readmepath ) or die ( "Can't opendir '$readmepath': $!\n" ) ;
2023-03-25 04:57:24 +08:00
while ( my $ d = readdir ( DH ) ) {
my $ dent = $ d ;
2023-03-01 00:37:46 +08:00
if ( $ dent =~ /\AREADME\-(.*?\.md)\Z/ ) { # we only bridge Markdown files here.
my $ wikifname = $ 1 ;
2023-03-01 00:55:19 +08:00
next if $ wikifname eq 'FrontPage.md' ;
2023-03-01 01:03:48 +08:00
filecopy ( "$readmepath/$dent" , "$wikireadmepath/$wikifname" , "\n" ) ;
2023-03-01 00:37:46 +08:00
}
}
closedir ( DH ) ;
2023-03-01 01:26:31 +08:00
my @ pages = ( ) ;
2023-03-01 00:37:46 +08:00
opendir ( DH , $ wikireadmepath ) or die ( "Can't opendir '$wikireadmepath': $!\n" ) ;
2023-03-25 04:57:24 +08:00
while ( my $ d = readdir ( DH ) ) {
my $ dent = $ d ;
2023-03-01 00:37:46 +08:00
if ( $ dent =~ /\A(.*?)\.(mediawiki|md)\Z/ ) {
my $ wikiname = $ 1 ;
2023-03-01 00:55:19 +08:00
next if $ wikiname eq 'FrontPage' ;
2023-03-01 01:26:31 +08:00
push @ pages , $ wikiname ;
2023-03-01 00:37:46 +08:00
}
}
closedir ( DH ) ;
2023-03-01 01:26:31 +08:00
open ( FH , '>' , "$wikireadmepath/FrontPage.md" ) or die ( "Can't open '$wikireadmepath/FrontPage.md': $!\n" ) ;
print FH "# All READMEs available here\n\n" ;
foreach ( sort @ pages ) {
my $ wikiname = $ _ ;
print FH "- [$wikiname]($wikiname)\n" ;
}
2023-03-01 00:37:46 +08:00
close ( FH ) ;
}
}
2022-01-07 04:37:05 +08:00
} elsif ( $ copy_direction == - 2 ) { # --copy-to-manpages
# This only takes from the wiki data, since it has sections we omit from the headers, like code examples.
2024-04-11 13:03:07 +08:00
File::Path:: make_path ( "$manpath/man3" ) ;
2022-01-07 04:37:05 +08:00
$ dewikify_mode = 'manpage' ;
$ wordwrap_mode = 'manpage' ;
my $ introtxt = '' ;
if ( 0 ) {
open ( FH , '<' , "$srcpath/LICENSE.txt" ) or die ( "Can't open '$srcpath/LICENSE.txt': $!\n" ) ;
while ( <FH> ) {
chomp ;
$ introtxt . = ".\\\" $_\n" ;
}
close ( FH ) ;
}
2023-09-05 00:56:00 +08:00
if ( ! $ gitrev ) {
$ gitrev = `cd "$srcpath" ; git rev-list HEAD~..` ;
chomp ( $ gitrev ) ;
}
2022-01-07 04:37:05 +08:00
2022-06-16 11:25:36 +08:00
# !!! FIXME
open ( FH , '<' , "$srcpath/$versionfname" ) or die ( "Can't open '$srcpath/$versionfname': $!\n" ) ;
2022-01-07 04:37:05 +08:00
my $ majorver = 0 ;
my $ minorver = 0 ;
2024-05-14 22:47:13 +08:00
my $ microver = 0 ;
2022-01-07 04:37:05 +08:00
while ( <FH> ) {
chomp ;
2022-06-16 11:25:36 +08:00
if ( /$versionmajorregex/ ) {
2022-01-07 04:37:05 +08:00
$ majorver = int ( $ 1 ) ;
2022-06-16 11:25:36 +08:00
} elsif ( /$versionminorregex/ ) {
2022-01-07 04:37:05 +08:00
$ minorver = int ( $ 1 ) ;
2024-05-14 22:47:13 +08:00
} elsif ( /$versionmicroregex/ ) {
$ microver = int ( $ 1 ) ;
2022-01-07 04:37:05 +08:00
}
}
close ( FH ) ;
2024-05-14 22:47:13 +08:00
my $ fullversion = "$majorver.$minorver.$microver" ;
2022-01-07 04:37:05 +08:00
2024-04-06 14:10:42 +08:00
foreach ( keys % headersyms ) {
my $ sym = $ _ ;
next if not defined $ wikisyms { $ sym } ; # don't have a page for that function, skip it.
2024-05-16 22:44:37 +08:00
next if $ sym =~ /\A\[category documentation\]/ ; # not real symbols
2024-04-09 10:34:11 +08:00
my $ symtype = $ headersymstype { $ sym } ;
2024-04-06 14:10:42 +08:00
my $ wikitype = $ wikitypes { $ sym } ;
my $ sectionsref = $ wikisyms { $ sym } ;
2023-03-25 04:57:24 +08:00
my $ remarks = $ sectionsref - > { 'Remarks' } ;
my $ params = $ sectionsref - > { 'Function Parameters' } ;
my $ returns = $ sectionsref - > { 'Return Value' } ;
my $ version = $ sectionsref - > { 'Version' } ;
my $ threadsafety = $ sectionsref - > { 'Thread Safety' } ;
2024-04-07 11:25:37 +08:00
my $ related = $ sectionsref - > { 'See Also' } ;
2023-03-25 04:57:24 +08:00
my $ examples = $ sectionsref - > { 'Code Examples' } ;
my $ deprecated = $ sectionsref - > { 'Deprecated' } ;
2024-04-01 00:56:00 +08:00
my $ headerfile = $ manpageheaderfiletext ;
2024-04-06 14:10:42 +08:00
$ headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g ;
2024-04-01 00:56:00 +08:00
$ headerfile . = "\n" ;
2024-04-11 13:03:07 +08:00
my $ mansection ;
my $ mansectionname ;
if ( ( $ symtype == 1 ) || ( $ symtype == 2 ) ) { # functions or macros
$ mansection = '3' ;
$ mansectionname = 'FUNCTIONS' ;
} elsif ( ( $ symtype >= 3 ) && ( $ symtype <= 5 ) ) { # struct/union/enum/typedef
$ mansection = '3type' ;
$ mansectionname = 'DATATYPES' ;
} else {
die ( "Unexpected symtype $symtype" ) ;
}
2023-03-25 04:57:24 +08:00
my $ brief = $ sectionsref - > { '[Brief]' } ;
2024-04-06 14:10:42 +08:00
my $ decl = $ headerdecls { $ sym } ;
2022-01-07 04:37:05 +08:00
my $ str = '' ;
$ brief = "$brief" ;
$ brief =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms ;
$ brief =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms ;
$ brief =~ s/\A(.*?\.) /$1\n/ ; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
my @ briefsplit = split /\n/ , $ brief ;
$ brief = shift @ briefsplit ;
$ brief = dewikify ( $ wikitype , $ brief ) ;
if ( defined $ remarks ) {
$ remarks = dewikify ( $ wikitype , join ( "\n" , @ briefsplit ) . $ remarks ) ;
}
$ str . = $ introtxt ;
$ str . = ".\\\" This manpage content is licensed under Creative Commons\n" ;
$ str . = ".\\\" Attribution 4.0 International (CC BY 4.0)\n" ;
$ str . = ".\\\" https://creativecommons.org/licenses/by/4.0/\n" ;
2024-04-06 14:10:42 +08:00
$ str . = ".\\\" This manpage was generated from ${projectshortname}'s wiki page for $sym:\n" ;
$ str . = ".\\\" $wikiurl/$sym\n" ;
2022-01-07 04:37:05 +08:00
$ str . = ".\\\" Generated with SDL/build-scripts/wikiheaders.pl\n" ;
$ str . = ".\\\" revision $gitrev\n" if $ gitrev ne '' ;
$ str . = ".\\\" Please report issues in this manpage's content at:\n" ;
2022-06-16 11:25:36 +08:00
$ str . = ".\\\" $bugreporturl\n" ;
2022-01-07 04:37:05 +08:00
$ str . = ".\\\" Please report issues in the generation of this manpage from the wiki at:\n" ;
2024-04-06 14:10:42 +08:00
$ str . = ".\\\" https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n" ;
2022-06-16 11:25:36 +08:00
$ str . = ".\\\" $projectshortname can be found at $projecturl\n" ;
2022-01-07 04:37:05 +08:00
2022-05-25 22:42:11 +08:00
# Define a .URL macro. The "www.tmac" thing decides if we're using GNU roff (which has a .URL macro already), and if so, overrides the macro we just created.
# This wizadry is from https://web.archive.org/web/20060102165607/http://people.debian.org/~branden/talks/wtfm/wtfm.pdf
$ str . = ".de URL\n" ;
$ str . = '\\$2 \(laURL: \\$1 \(ra\\$3' . "\n" ;
$ str . = "..\n" ;
$ str . = '.if \n[.g] .mso www.tmac' . "\n" ;
2024-04-11 13:03:07 +08:00
$ str . = ".TH $sym $mansection \"$projectshortname $fullversion\" \"$projectfullname\" \"$projectshortname$majorver $mansectionname\"\n" ;
2022-01-07 04:37:05 +08:00
$ str . = ".SH NAME\n" ;
2024-04-06 14:10:42 +08:00
$ str . = "$sym" ;
2022-01-07 04:37:05 +08:00
$ str . = " \\- $brief" if ( defined $ brief ) ;
$ str . = "\n" ;
2024-04-01 00:56:00 +08:00
if ( defined $ deprecated ) {
$ str . = ".SH DEPRECATED\n" ;
$ str . = dewikify ( $ wikitype , $ deprecated ) . "\n" ;
}
if ( defined $ headerfile ) {
$ str . = ".SH HEADER FILE\n" ;
$ str . = dewikify ( $ wikitype , $ headerfile ) . "\n" ;
}
2022-01-07 04:37:05 +08:00
$ str . = ".SH SYNOPSIS\n" ;
$ str . = ".nf\n" ;
2022-06-16 11:25:36 +08:00
$ str . = ".B #include \\(dq$mainincludefname\\(dq\n" ;
2022-01-07 04:37:05 +08:00
$ str . = ".PP\n" ;
my @ decllines = split /\n/ , $ decl ;
foreach ( @ decllines ) {
$ str . = ".BI \"$_\n" ;
}
$ str . = ".fi\n" ;
if ( defined $ remarks ) {
$ str . = ".SH DESCRIPTION\n" ;
$ str . = $ remarks . "\n" ;
}
if ( defined $ params ) {
2024-04-11 12:36:10 +08:00
if ( ( $ symtype == 1 ) || ( $ symtype == 5 ) ) {
2024-04-09 10:34:11 +08:00
$ str . = ".SH FUNCTION PARAMETERS\n" ;
} elsif ( $ symtype == 2 ) { # macro
$ str . = ".SH MACRO PARAMETERS\n" ;
} elsif ( $ symtype == 3 ) { # struct/union
$ str . = ".SH FIELDS\n" ;
} elsif ( $ symtype == 4 ) { # enum
$ str . = ".SH VALUES\n" ;
} else {
die ( "Unexpected symtype $symtype" ) ;
}
2022-01-07 04:37:05 +08:00
my @ lines = split /\n/ , $ params ;
if ( $ wikitype eq 'mediawiki' ) {
die ( "Unexpected data parsing MediaWiki table" ) if ( shift @ lines ne '{|' ) ; # Dump the '{|' start
while ( scalar ( @ lines ) >= 3 ) {
2024-06-13 03:49:59 +08:00
my $ c_datatype = shift @ lines ;
2022-01-07 04:37:05 +08:00
my $ name = shift @ lines ;
my $ desc = shift @ lines ;
2024-06-13 03:49:59 +08:00
my $ terminator ; # the '|-' or '|}' line.
if ( ( $ desc eq '|-' ) or ( $ desc eq '|}' ) or ( not $ desc =~ /\A\|/ ) ) { # we seem to be out of cells, which means there was no datatype column on this one.
$ terminator = $ desc ;
$ desc = $ name ;
$ name = $ c_datatype ;
$ c_datatype = '' ;
} else {
$ terminator = shift @ lines ;
}
2022-01-07 04:37:05 +08:00
last if ( $ terminator ne '|-' ) and ( $ terminator ne '|}' ) ; # we seem to have run out of table.
$ name =~ s/\A\|\s*// ;
$ name =~ s/\A\*\*(.*?)\*\*/$1/ ;
$ name =~ s/\A\'\'\'(.*?)\'\'\'/$1/ ;
$ desc =~ s/\A\|\s*// ;
$ desc = dewikify ( $ wikitype , $ desc ) ;
2024-06-13 03:49:59 +08:00
#print STDERR "SYM: $sym CDATATYPE: $c_datatype NAME: $name DESC: $desc TERM: $terminator\n";
2022-01-07 04:37:05 +08:00
$ str . = ".TP\n" ;
$ str . = ".I $name\n" ;
$ str . = "$desc\n" ;
}
2023-02-24 23:21:32 +08:00
} elsif ( $ wikitype eq 'md' ) {
my $ l ;
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
die ( "Unexpected data parsing Markdown table" ) if ( not $ l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/ ) ;
2023-02-24 23:21:32 +08:00
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
die ( "Unexpected data parsing Markdown table" ) if ( not $ l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/ ) ;
2023-02-24 23:21:32 +08:00
while ( scalar ( @ lines ) >= 1 ) {
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
my $ name ;
my $ desc ;
if ( $ l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/ ) {
# c datatype is $1, but we don't care about it here.
$ name = $ 2 ;
$ desc = $ 3 ;
} elsif ( $ l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/ ) {
$ name = $ 1 ;
$ desc = $ 2 ;
2023-02-24 23:21:32 +08:00
} else {
last ; # we seem to have run out of table.
}
2024-06-13 03:49:59 +08:00
$ name =~ s/\A\*\*(.*?)\*\*/$1/ ;
$ name =~ s/\A\'\'\'(.*?)\'\'\'/$1/ ;
$ desc = dewikify ( $ wikitype , $ desc ) ;
$ str . = ".TP\n" ;
$ str . = ".I $name\n" ;
$ str . = "$desc\n" ;
2023-02-24 23:21:32 +08:00
}
2022-01-07 04:37:05 +08:00
} else {
die ( "write me" ) ;
}
}
if ( defined $ returns ) {
2024-06-13 03:49:59 +08:00
$ returns = dewikify ( $ wikitype , $ returns ) ;
$ returns =~ s/\A\(.*?\)\s*// ; # Chop datatype in parentheses off the front.
2022-01-07 04:37:05 +08:00
$ str . = ".SH RETURN VALUE\n" ;
2024-06-13 03:49:59 +08:00
$ str . = "$returns\n" ;
2022-01-07 04:37:05 +08:00
}
if ( defined $ examples ) {
$ str . = ".SH CODE EXAMPLES\n" ;
$ dewikify_manpage_code_indent = 0 ;
$ str . = dewikify ( $ wikitype , $ examples ) . "\n" ;
$ dewikify_manpage_code_indent = 1 ;
}
2023-01-25 11:13:25 +08:00
if ( defined $ threadsafety ) {
$ str . = ".SH THREAD SAFETY\n" ;
$ str . = dewikify ( $ wikitype , $ threadsafety ) . "\n" ;
}
2022-01-07 04:37:05 +08:00
if ( defined $ version ) {
$ str . = ".SH AVAILABILITY\n" ;
$ str . = dewikify ( $ wikitype , $ version ) . "\n" ;
}
if ( defined $ related ) {
$ str . = ".SH SEE ALSO\n" ;
# !!! FIXME: lots of code duplication in all of these.
my $ v = dewikify ( $ wikitype , $ related ) ;
my @ desclines = split /\n/ , $ v ;
my $ nextstr = '' ;
foreach ( @ desclines ) {
s/\(\)\Z// ; # Convert "SDL_Func()" to "SDL_Func"
2022-06-19 12:15:03 +08:00
s/\[\[(.*?)\]\]/$1/ ; # in case some wikilinks remain.
2023-02-25 00:45:43 +08:00
s/\[(.*?)\]\(.*?\)/$1/ ; # in case some wikilinks remain.
2023-02-24 23:21:32 +08:00
s/\A\*\s*\Z// ;
2022-06-16 11:25:36 +08:00
s/\A\/*// ;
2022-01-07 04:37:05 +08:00
s/\A\.BR\s+// ; # dewikify added this, but we want to handle it.
2023-02-24 23:21:32 +08:00
s/\A\.I\s+// ; # dewikify added this, but we want to handle it.
2024-05-08 02:23:02 +08:00
s/\A\s*[\:\*\-]\s*// ;
2022-01-07 04:37:05 +08:00
s/\A\s+// ;
s/\s+\Z// ;
next if $ _ eq '' ;
2024-05-01 09:35:35 +08:00
my $ seealso_symtype = $ headersymstype { $ _ } ;
my $ seealso_mansection = '3' ;
if ( defined ( $ seealso_symtype ) && ( $ seealso_symtype >= 3 ) && ( $ seealso_symtype <= 5 ) ) { # struct/union/enum/typedef
$ seealso_mansection = '3type' ;
}
$ str . = "$nextstr.BR $_ ($seealso_mansection)" ;
2022-01-07 04:37:05 +08:00
$ nextstr = ",\n" ;
}
$ str . = "\n" ;
}
if ( 0 ) {
$ str . = ".SH COPYRIGHT\n" ;
$ str . = "This manpage is licensed under\n" ;
$ str . = ".UR https://creativecommons.org/licenses/by/4.0/\n" ;
$ str . = "Creative Commons Attribution 4.0 International (CC BY 4.0)\n" ;
$ str . = ".UE\n" ;
$ str . = ".PP\n" ;
$ str . = "This manpage was generated from\n" ;
2024-04-06 14:10:42 +08:00
$ str . = ".UR $wikiurl/$sym\n" ;
2022-06-16 11:25:36 +08:00
$ str . = "${projectshortname}'s wiki\n" ;
2022-01-07 04:37:05 +08:00
$ str . = ".UE\n" ;
$ str . = "using SDL/build-scripts/wikiheaders.pl" ;
$ str . = " revision $gitrev" if $ gitrev ne '' ;
$ str . = ".\n" ;
$ str . = "Please report issues in this manpage at\n" ;
2022-06-16 11:25:36 +08:00
$ str . = ".UR $bugreporturl\n" ;
2022-01-07 04:37:05 +08:00
$ str . = "our bugtracker!\n" ;
$ str . = ".UE\n" ;
}
2024-05-01 09:21:04 +08:00
my $ path = "$manpath/man3/$_.$mansection" ;
2024-04-11 12:36:10 +08:00
my $ tmppath = "$path.tmp" ;
open ( FH , '>' , $ tmppath ) or die ( "Can't open '$tmppath': $!\n" ) ;
2022-01-07 04:37:05 +08:00
print FH $ str ;
close ( FH ) ;
2024-04-11 12:36:10 +08:00
rename ( $ tmppath , $ path ) or die ( "Can't rename '$tmppath' to '$path': $!\n" ) ;
2022-01-07 04:37:05 +08:00
}
2024-05-05 14:03:44 +08:00
} elsif ( $ copy_direction == - 4 ) { # --copy-to-latex
# This only takes from the wiki data, since it has sections we omit from the headers, like code examples.
print STDERR "\n(The --copy-to-latex code is known to not be ready for serious use; send patches, not bug reports, please.)\n\n" ;
$ dewikify_mode = 'LaTeX' ;
$ wordwrap_mode = 'LaTeX' ;
# !!! FIXME: code duplication with --copy-to-manpages section.
my $ introtxt = '' ;
if ( 0 ) {
open ( FH , '<' , "$srcpath/LICENSE.txt" ) or die ( "Can't open '$srcpath/LICENSE.txt': $!\n" ) ;
while ( <FH> ) {
chomp ;
$ introtxt . = ".\\\" $_\n" ;
}
close ( FH ) ;
}
if ( ! $ gitrev ) {
$ gitrev = `cd "$srcpath" ; git rev-list HEAD~..` ;
chomp ( $ gitrev ) ;
}
# !!! FIXME
open ( FH , '<' , "$srcpath/$versionfname" ) or die ( "Can't open '$srcpath/$versionfname': $!\n" ) ;
my $ majorver = 0 ;
my $ minorver = 0 ;
2024-05-14 22:47:13 +08:00
my $ microver = 0 ;
2024-05-05 14:03:44 +08:00
while ( <FH> ) {
chomp ;
if ( /$versionmajorregex/ ) {
$ majorver = int ( $ 1 ) ;
} elsif ( /$versionminorregex/ ) {
$ minorver = int ( $ 1 ) ;
2024-05-14 22:47:13 +08:00
} elsif ( /$versionmicroregex/ ) {
$ microver = int ( $ 1 ) ;
2024-05-05 14:03:44 +08:00
}
}
close ( FH ) ;
2024-05-14 22:47:13 +08:00
my $ fullversion = "$majorver.$minorver.$microver" ;
2024-05-05 14:03:44 +08:00
my $ latex_fname = "$srcpath/$projectshortname.tex" ;
my $ latex_tmpfname = "$latex_fname.tmp" ;
open ( TEXFH , '>' , "$latex_tmpfname" ) or die ( "Can't open '$latex_tmpfname' for writing: $!\n" ) ;
print TEXFH << __EOF__
\ \ documentclass { book }
\ \ usepackage { listings }
\ \ usepackage { color }
\ \ usepackage { hyperref }
\ \ definecolor { dkgreen } { rgb } { 0 , 0.6 , 0 }
\ \ definecolor { gray } { rgb } { 0.5 , 0.5 , 0.5 }
\ \ definecolor { mauve } { rgb } { 0.58 , 0 , 0.82 }
\ \ setcounter { secnumdepth } { 0 }
\ \ lstset { frame = tb ,
language = C ,
aboveskip = 3 mm ,
belowskip = 3 mm ,
showstringspaces = false ,
columns = flexible ,
basicstyle = { \ \ small \ \ ttfamily } ,
numbers = none ,
numberstyle = \ \ tiny \ \ color { gray } ,
keywordstyle = \ \ color { blue } ,
commentstyle = \ \ color { dkgreen } ,
stringstyle = \ \ color { mauve } ,
breaklines = true ,
breakatwhitespace = true ,
tabsize = 3
}
\ \ begin { document }
\ \ frontmatter
2024-05-14 22:47:13 +08:00
\ \ title { $ projectfullname $ majorver . $ minorver . $ microver Reference Manual }
2024-05-05 14:03:44 +08:00
\ \ author { The $ projectshortname Developers }
\ \ maketitle
\ \ mainmatter
__EOF__
;
# !!! FIXME: Maybe put this in the book intro? print TEXFH $introtxt;
# Sort symbols by symbol type, then alphabetically.
my @ headersymskeys = sort {
2024-05-05 14:44:45 +08:00
my $ symtypea = $ headersymstype { $ a } ;
my $ symtypeb = $ headersymstype { $ b } ;
$ symtypea = 3 if ( $ symtypea > 3 ) ;
$ symtypeb = 3 if ( $ symtypeb > 3 ) ;
my $ rc = $ symtypea <=> $ symtypeb ;
2024-05-05 14:03:44 +08:00
if ( $ rc == 0 ) {
2024-05-05 14:44:45 +08:00
$ rc = lc ( $ a ) cmp lc ( $ b ) ;
2024-05-05 14:03:44 +08:00
}
return $ rc ;
} keys % headersyms ;
my $ current_symtype = 0 ;
2024-05-05 14:44:45 +08:00
my $ current_chapter = '' ;
2024-05-05 14:03:44 +08:00
foreach ( @ headersymskeys ) {
my $ sym = $ _ ;
next if not defined $ wikisyms { $ sym } ; # don't have a page for that function, skip it.
2024-05-16 22:44:37 +08:00
next if $ sym =~ /\A\[category documentation\]/ ; # not real symbols.
2024-05-05 14:03:44 +08:00
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 $ version = $ sectionsref - > { 'Version' } ;
my $ threadsafety = $ sectionsref - > { 'Thread Safety' } ;
my $ related = $ sectionsref - > { 'See Also' } ;
my $ examples = $ sectionsref - > { 'Code Examples' } ;
my $ deprecated = $ sectionsref - > { 'Deprecated' } ;
my $ headerfile = $ manpageheaderfiletext ;
$ headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g ;
$ headerfile . = "\n" ;
my $ brief = $ sectionsref - > { '[Brief]' } ;
my $ decl = $ headerdecls { $ sym } ;
my $ str = '' ;
if ( $ current_symtype != $ symtype ) {
my $ newchapter = '' ;
if ( $ symtype == 1 ) {
$ newchapter = 'Functions' ;
} elsif ( $ symtype == 2 ) {
$ newchapter = 'Macros' ;
} else {
$ newchapter = 'Datatypes' ;
}
2024-05-05 14:44:45 +08:00
if ( $ current_chapter ne $ newchapter ) {
$ str . = "\n\n\\chapter{$projectshortname $newchapter}\n\n\\clearpage\n\n" ;
$ current_chapter = $ newchapter ;
}
2024-05-05 14:03:44 +08:00
$ current_symtype = $ symtype ;
}
$ brief = "$brief" ;
$ brief =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms ;
$ brief =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms ;
$ brief =~ s/\A(.*?\.) /$1\n/ ; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
my @ briefsplit = split /\n/ , $ brief ;
$ brief = shift @ briefsplit ;
$ brief = dewikify ( $ wikitype , $ brief ) ;
if ( defined $ remarks ) {
$ remarks = dewikify ( $ wikitype , join ( "\n" , @ briefsplit ) . $ remarks ) ;
}
my $ escapedsym = escLaTeX ( $ sym ) ;
$ str . = "\\hypertarget{$sym}{%\n\\section{$escapedsym}\\label{$sym}}\n\n" ;
$ str . = $ brief if ( defined $ brief ) ;
$ str . = "\n\n" ;
if ( defined $ deprecated ) {
$ str . = "\\subsection{Deprecated}\n\n" ;
$ str . = dewikify ( $ wikitype , $ deprecated ) . "\n" ;
}
if ( defined $ headerfile ) {
$ str . = "\\subsection{Header File}\n\n" ;
$ str . = dewikify ( $ wikitype , $ headerfile ) . "\n" ;
}
$ str . = "\\subsection{Syntax}\n\n" ;
$ str . = "\\begin{lstlisting}\n$decl\n\\end{lstlisting}\n" ;
if ( defined $ params ) {
if ( ( $ symtype == 1 ) || ( $ symtype == 5 ) ) {
$ str . = "\\subsection{Function Parameters}\n\n" ;
} elsif ( $ symtype == 2 ) { # macro
$ str . = "\\subsection{Macro Parameters}\n\n" ;
} elsif ( $ symtype == 3 ) { # struct/union
$ str . = "\\subsection{Fields}\n\n" ;
} elsif ( $ symtype == 4 ) { # enum
$ str . = "\\subsection{Values}\n\n" ;
} else {
die ( "Unexpected symtype $symtype" ) ;
}
$ str . = "\\begin{center}\n" ;
$ str . = " \\begin{tabular}{ | l | p{0.75\\textwidth} |}\n" ;
$ str . = " \\hline\n" ;
2024-06-13 03:49:59 +08:00
# !!! FIXME: this table parsing has gotten complicated and is pasted three times in this file; move it to a subroutine!
2024-05-05 14:03:44 +08:00
my @ lines = split /\n/ , $ params ;
if ( $ wikitype eq 'mediawiki' ) {
die ( "Unexpected data parsing MediaWiki table" ) if ( shift @ lines ne '{|' ) ; # Dump the '{|' start
while ( scalar ( @ lines ) >= 3 ) {
my $ name = shift @ lines ;
my $ desc = shift @ lines ;
my $ terminator = shift @ lines ; # the '|-' or '|}' line.
last if ( $ terminator ne '|-' ) and ( $ terminator ne '|}' ) ; # we seem to have run out of table.
$ name =~ s/\A\|\s*// ;
$ name =~ s/\A\*\*(.*?)\*\*/$1/ ;
$ name =~ s/\A\'\'\'(.*?)\'\'\'/$1/ ;
$ name = escLaTeX ( $ name ) ;
$ desc =~ s/\A\|\s*// ;
$ desc = dewikify ( $ wikitype , $ desc ) ;
#print STDERR "FN: $sym NAME: $name DESC: $desc TERM: $terminator\n";
$ str . = " \\textbf{$name} & $desc \\\\ \\hline\n" ;
}
} elsif ( $ wikitype eq 'md' ) {
my $ l ;
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
die ( "Unexpected data parsing Markdown table" ) if ( not $ l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/ ) ;
2024-05-05 14:03:44 +08:00
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
die ( "Unexpected data parsing Markdown table" ) if ( not $ l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/ ) ;
2024-05-05 14:03:44 +08:00
while ( scalar ( @ lines ) >= 1 ) {
$ l = shift @ lines ;
2024-06-13 03:49:59 +08:00
my $ name ;
my $ desc ;
if ( $ l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/ ) {
# c datatype is $1, but we don't care about it here.
$ name = $ 2 ;
$ desc = $ 3 ;
} elsif ( $ l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/ ) {
$ name = $ 1 ;
$ desc = $ 2 ;
2024-05-05 14:03:44 +08:00
} else {
last ; # we seem to have run out of table.
}
2024-06-13 03:49:59 +08:00
$ name =~ s/\A\*\*(.*?)\*\*/$1/ ;
$ name =~ s/\A\'\'\'(.*?)\'\'\'/$1/ ;
$ name = escLaTeX ( $ name ) ;
$ desc = dewikify ( $ wikitype , $ desc ) ;
$ str . = " \\textbf{$name} & $desc \\\\ \\hline\n" ;
2024-05-05 14:03:44 +08:00
}
} else {
die ( "write me" ) ;
}
$ str . = " \\end{tabular}\n" ;
$ str . = "\\end{center}\n" ;
}
if ( defined $ returns ) {
2024-06-13 03:49:59 +08:00
$ returns = dewikify ( $ wikitype , $ returns ) ;
$ returns =~ s/\A\(.*?\)\s*// ; # Chop datatype in parentheses off the front.
2024-05-05 14:03:44 +08:00
$ str . = "\\subsection{Return Value}\n\n" ;
2024-06-13 03:49:59 +08:00
$ str . = "$returns\n" ;
2024-05-05 14:03:44 +08:00
}
if ( defined $ remarks ) {
$ str . = "\\subsection{Remarks}\n\n" ;
$ str . = $ remarks . "\n" ;
}
if ( defined $ examples ) {
$ str . = "\\subsection{Code Examples}\n\n" ;
$ dewikify_manpage_code_indent = 0 ;
$ str . = dewikify ( $ wikitype , $ examples ) . "\n" ;
$ dewikify_manpage_code_indent = 1 ;
}
if ( defined $ threadsafety ) {
$ str . = "\\subsection{Thread Safety}\n\n" ;
$ str . = dewikify ( $ wikitype , $ threadsafety ) . "\n" ;
}
if ( defined $ version ) {
$ str . = "\\subsection{Version}\n\n" ;
$ str . = dewikify ( $ wikitype , $ version ) . "\n" ;
}
if ( defined $ related ) {
$ str . = "\\subsection{See Also}\n\n" ;
$ str . = "\\begin{itemize}\n" ;
# !!! FIXME: lots of code duplication in all of these.
my $ v = dewikify ( $ wikitype , $ related ) ;
my @ desclines = split /\n/ , $ v ;
my $ nextstr = '' ;
foreach ( @ desclines ) {
s/\(\)\Z// ; # Convert "SDL_Func()" to "SDL_Func"
s/\[\[(.*?)\]\]/$1/ ; # in case some wikilinks remain.
s/\[(.*?)\]\(.*?\)/$1/ ; # in case some wikilinks remain.
s/\A\*\s*\Z// ;
s/\A\s*\\item\s*// ;
s/\A\/*// ;
2024-05-08 02:23:02 +08:00
s/\A\s*[\:\*\-]\s*// ;
2024-05-05 14:03:44 +08:00
s/\A\s+// ;
s/\s+\Z// ;
next if $ _ eq '' ;
next if $ _ eq '\begin{itemize}' ;
next if $ _ eq '\end{itemize}' ;
$ str . = " \\item $_\n" ;
}
$ str . = "\\end{itemize}\n" ;
$ str . = "\n" ;
}
# !!! FIXME: Maybe put copyright in the book intro?
if ( 0 ) {
$ str . = ".SH COPYRIGHT\n" ;
$ str . = "This manpage is licensed under\n" ;
$ str . = ".UR https://creativecommons.org/licenses/by/4.0/\n" ;
$ str . = "Creative Commons Attribution 4.0 International (CC BY 4.0)\n" ;
$ str . = ".UE\n" ;
$ str . = ".PP\n" ;
$ str . = "This manpage was generated from\n" ;
$ str . = ".UR $wikiurl/$sym\n" ;
$ str . = "${projectshortname}'s wiki\n" ;
$ str . = ".UE\n" ;
$ str . = "using SDL/build-scripts/wikiheaders.pl" ;
$ str . = " revision $gitrev" if $ gitrev ne '' ;
$ str . = ".\n" ;
$ str . = "Please report issues in this manpage at\n" ;
$ str . = ".UR $bugreporturl\n" ;
$ str . = "our bugtracker!\n" ;
$ str . = ".UE\n" ;
}
$ str . = "\\clearpage\n\n" ;
print TEXFH $ str ;
}
print TEXFH "\\end{document}\n\n" ;
close ( TEXFH ) ;
rename ( $ latex_tmpfname , $ latex_fname ) or die ( "Can't rename '$latex_tmpfname' to '$latex_fname': $!\n" ) ;
} elsif ( $ copy_direction == - 3 ) { # --report-coverage-gaps
2024-05-02 07:58:21 +08:00
foreach ( @ coverage_gap ) {
print ( "$_\n" ) ;
}
2021-03-17 01:46:03 +08:00
}
# end of wikiheaders.pl ...