2021-03-16 13:46:03 -04:00
#!/usr/bin/perl -w
2024-07-22 22:38:40 -04:00
# Simple DirectMedia Layer
2026-01-01 09:39:50 -08:00
# Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
2024-07-22 22:38:40 -04:00
#
# 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-16 13:46:03 -04:00
use warnings ;
use strict ;
2023-03-18 00:25:44 +01:00
use File::Path ;
2021-03-16 13:46:03 -04:00
use Text::Wrap ;
2022-05-25 09:30:29 -04:00
$ Text::Wrap:: huge = 'overflow' ;
2022-06-15 23:25:36 -04:00
my $projectfullname = 'Simple Directmedia Layer' ;
my $projectshortname = 'SDL' ;
my $wikisubdir = '' ;
my $incsubdir = 'include' ;
2023-02-28 11:37:46 -05:00
my $readmesubdir = undef ;
2022-06-15 23:25:36 -04:00
my $apiprefixregex = undef ;
2025-06-15 22:32:15 -04:00
my $apipropertyregex = undef ;
2024-05-15 14:22:41 -07:00
my $versionfname = 'include/SDL_version.h' ;
2022-06-15 23:25:36 -04: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 07:47:13 -07:00
my $versionmicroregex = '\A\#define\s+SDL_MICRO_VERSION\s+(\d+)\Z' ;
2025-11-25 23:11:13 -05:00
my $wikidocsectionsym = 'SDL_WIKI_DOCUMENTATION_SECTION' ;
my $forceinlinesym = 'SDL_FORCE_INLINE' ;
my $deprecatedsym = 'SDL_DEPRECATED' ;
my $declspecsym = '(?:SDLMAIN_|SDL_)?DECLSPEC' ;
my $callconvsym = 'SDLCALL' ;
2022-06-15 23:25:36 -04: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-16 13:46:03 -04:00
my $srcpath = undef ;
my $wikipath = undef ;
my $warn_about_missing = 0 ;
my $copy_direction = 0 ;
2022-06-15 23:25:36 -04:00
my $optionsfname = undef ;
2022-06-17 14:39:50 -04:00
my $wikipreamble = undef ;
2024-03-31 12:56:00 -04:00
my $wikiheaderfiletext = 'Defined in %fname%' ;
my $manpageheaderfiletext = 'Defined in %fname%' ;
2025-01-09 14:12:07 -05:00
my $manpagesymbolfilterregex = undef ;
2024-05-14 10:19:34 -04:00
my $headercategoryeval = undef ;
2024-12-10 11:21:57 -05:00
my $quickrefenabled = 0 ;
my @quickrefcategoryorder ;
my $quickreftitle = undef ;
my $quickrefurl = undef ;
my $quickrefdesc = undef ;
my $quickrefmacroregex = undef ;
2025-09-02 15:49:52 -04:00
my $envvarenabled = 0 ;
my $envvartitle = 'Environment Variables' ;
my $envvardesc = undef ;
my $envvarsymregex = undef ;
my $envvarsymreplace = undef ;
2023-02-23 19:53:53 -05:00
my $changeformat = undef ;
2023-03-18 00:25:44 +01:00
my $manpath = undef ;
2023-09-04 18:56:00 +02:00
my $gitrev = undef ;
2021-03-16 13:46:03 -04:00
foreach ( @ARGV ) {
$warn_about_missing = 1 , next if $_ eq '--warn-about-missing' ;
$copy_direction = 1 , next if $_ eq '--copy-to-headers' ;
2021-07-14 17:07:41 -04:00
$copy_direction = 1 , next if $_ eq '--copy-to-header' ;
2021-03-16 13:46:03 -04:00
$copy_direction = - 1 , next if $_ eq '--copy-to-wiki' ;
2022-01-06 15:37:05 -05:00
$copy_direction = - 2 , next if $_ eq '--copy-to-manpages' ;
2024-05-01 19:58:21 -04:00
$copy_direction = - 3 , next if $_ eq '--report-coverage-gaps' ;
2024-05-05 02:03:44 -04:00
$copy_direction = - 4 , next if $_ eq '--copy-to-latex' ;
2022-06-15 23:25:36 -04:00
if ( /\A--options=(.*)\Z/ ) {
$optionsfname = $1 ;
next ;
2023-02-23 19:53:53 -05:00
} elsif ( /\A--changeformat=(.*)\Z/ ) {
$changeformat = $1 ;
next ;
2023-03-18 00:25:44 +01:00
} elsif ( /\A--manpath=(.*)\Z/ ) {
$manpath = $1 ;
next ;
2023-09-04 18:56:00 +02:00
} elsif ( /\A--rev=(.*)\Z/ ) {
$gitrev = $1 ;
next ;
2022-06-15 23:25:36 -04:00
}
2021-03-16 13:46:03 -04:00
$srcpath = $_ , next if not defined $srcpath ;
$wikipath = $_ , next if not defined $wikipath ;
}
2022-06-15 23:25:36 -04: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 10:19:34 -04:00
next if /\A\s*\#/ ; # Skip lines that start with (optional whitespace, then) '#' as comments.
2022-06-15 23:25:36 -04: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' ;
2025-06-15 22:32:15 -04:00
$apipropertyregex = $val , next if $key eq 'apipropertyregex' ;
2022-06-15 23:25:36 -04:00
$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-02-28 11:37:46 -05:00
$readmesubdir = $val , next if $key eq 'readmesubdir' ;
2022-06-15 23:25:36 -04:00
$versionmajorregex = $val , next if $key eq 'versionmajorregex' ;
$versionminorregex = $val , next if $key eq 'versionminorregex' ;
2024-05-14 07:47:13 -07:00
$versionmicroregex = $val , next if $key eq 'versionmicroregex' ;
2022-06-15 23:25:36 -04: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-17 14:39:50 -04:00
$wikipreamble = $val , next if $key eq 'wikipreamble' ;
2024-03-31 12:56:00 -04:00
$wikiheaderfiletext = $val , next if $key eq 'wikiheaderfiletext' ;
$manpageheaderfiletext = $val , next if $key eq 'manpageheaderfiletext' ;
2025-01-09 14:12:07 -05:00
$manpagesymbolfilterregex = $val , next if $key eq 'manpagesymbolfilterregex' ;
2024-05-14 10:19:34 -04:00
$headercategoryeval = $val , next if $key eq 'headercategoryeval' ;
2024-12-10 11:21:57 -05:00
$quickrefenabled = int ( $val ), next if $key eq 'quickrefenabled' ;
@quickrefcategoryorder = split ( /,/ , $val ), next if $key eq 'quickrefcategoryorder' ;
$quickreftitle = $val , next if $key eq 'quickreftitle' ;
$quickrefurl = $val , next if $key eq 'quickrefurl' ;
$quickrefdesc = $val , next if $key eq 'quickrefdesc' ;
$quickrefmacroregex = $val , next if $key eq 'quickrefmacroregex' ;
2025-09-02 15:49:52 -04:00
$envvarenabled = int ( $val ), next if $key eq 'envvarenabled' ;
$envvartitle = $val , next if $key eq 'envvartitle' ;
$envvardesc = $val , next if $key eq 'envvardesc' ;
$envvarsymregex = $val , next if $key eq 'envvarsymregex' ;
$envvarsymreplace = $val , next if $key eq 'envvarsymreplace' ;
2025-11-25 23:11:13 -05:00
$wikidocsectionsym = $val , next if $key eq 'wikidocsectionsym' ;
$forceinlinesym = $val , next if $key eq 'forceinlinesym' ;
$deprecatedsym = $val , next if $key eq 'deprecatedsym' ;
$declspecsym = $val , next if $key eq 'declspecsym' ;
$callconvsym = $val , next if $key eq 'callconvsym' ;
2022-06-15 23:25:36 -04:00
}
}
close ( OPTIONS );
}
2024-05-05 02:03:44 -04:00
sub escLaTeX {
my $str = shift ;
$str =~ s/([_\#\&\^])/\\$1/g ;
return $str ;
}
2021-03-24 10:45:27 -04:00
my $wordwrap_mode = 'mediawiki' ;
sub wordwrap_atom { # don't call this directly.
my $str = shift ;
2022-05-25 10:42:11 -04: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 10:45:27 -04:00
}
sub wordwrap_with_bullet_indent { # don't call this directly.
my $bullet = shift ;
my $str = shift ;
my $retval = '' ;
2021-07-14 10:03:31 -04:00
#print("WORDWRAP BULLET ('$bullet'):\n\n$str\n\n");
2021-03-24 10:45:27 -04: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 10:03:31 -04:00
$retval =~ s/\s+$//gms ;
#print("WORDWRAP BULLET DONE:\n\n$retval\n\n");
2021-03-24 10:45:27 -04: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-23 19:53:53 -05:00
s/\s*\Z// ;
2021-03-24 10:45:27 -04: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-08 23:48:25 -04: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 10:45:27 -04: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-16 13:46:03 -04: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 08:11:18 -04:00
#print("\n\nWORDWRAP:\n\n$str\n\n\n");
2021-07-14 10:03:31 -04:00
$str =~ s/\A\n+//ms ;
2021-07-14 08:11:18 -04: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 10:45:27 -04:00
$retval .= wordwrap_paragraphs ( $1 ); # wrap it.
2021-07-14 08:11:18 -04:00
$retval .= "$2\n\n" ; # don't wrap it.
2021-03-16 13:46:03 -04:00
}
2021-07-13 11:11:33 -04:00
$retval .= wordwrap_paragraphs ( $str ); # wrap what's left.
2021-07-14 11:58:57 -04:00
$retval =~ s/\n+\Z//ms ;
2021-07-14 08:11:18 -04:00
#print("\n\nWORDWRAP DONE:\n\n$retval\n\n\n");
2021-07-13 11:11:33 -04:00
return $retval ;
2021-03-16 13:46:03 -04:00
}
2021-07-14 08:11:18 -04: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-16 13:46:03 -04:00
my $wikitype = shift ;
my $str = shift ;
2021-07-14 08:11:18 -04:00
my $codelang = shift ;
my $code = shift ;
#print("\n\nWIKIFY CHUNK:\n\n$str\n\n\n");
2021-03-16 13:46:03 -04:00
if ( $wikitype eq 'mediawiki' ) {
2021-09-30 17:34:25 -04: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-15 23:25:36 -04:00
if ( defined $apiprefixregex ) {
# Convert obvious API things to wikilinks, even inside `code` blocks.
2024-10-04 15:33:41 -04:00
$codeblock =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[\[$2\]\]/gms ;
2022-06-15 23:25:36 -04:00
}
2021-09-30 17:34:25 -04:00
$codedstr .= "<code>$codeblock</code>" ;
}
2022-06-15 23:25:36 -04:00
# Convert obvious API things to wikilinks.
if ( defined $apiprefixregex ) {
2024-10-04 15:33:41 -04:00
$str =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[\[$2\]\]/gms ;
2022-06-15 23:25:36 -04:00
}
2021-03-16 13:46:03 -04:00
# Make some Markdown things into MediaWiki...
2022-05-25 10:42:11 -04:00
# links
$str =~ s/\[(.*?)\]\((https?\:\/\/.*?)\)/\[$2 $1\]/g ;
2021-03-16 13:46:03 -04:00
# bold+italic
2021-03-25 12:52:15 -04:00
$str =~ s/\*\*\*(.*?)\*\*\*/'''''$1'''''/gms ;
2021-03-16 13:46:03 -04:00
# bold
$str =~ s/\*\*(.*?)\*\*/'''$1'''/gms ;
# italic
$str =~ s/\*(.*?)\*/''$1''/gms ;
2021-03-24 10:45:27 -04:00
# bullets
$str =~ s/^\- /* /gm ;
2021-07-14 08:11:18 -04:00
2021-09-30 17:34:25 -04:00
$str = $codedstr . $str ;
2021-07-14 08:11:18 -04:00
if ( defined $code ) {
$str .= "<syntaxhighlight lang='$codelang'>$code<\/syntaxhighlight>" ;
}
2021-03-16 13:46:03 -04:00
} elsif ( $wikitype eq 'md' ) {
2023-02-27 23:07:43 -05: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 ;
2022-06-15 23:25:36 -04:00
}
2023-02-27 23:07:43 -05:00
# Convert obvious API things to wikilinks.
if ( defined $apiprefixregex ) {
2025-08-26 14:43:59 -04:00
$str =~ s/(\A|[^\/a-zA-Z0-9_\[])($apiprefixregex[a-zA-Z0-9_]+)/$1\[$2\]\($2\)/gms ;
2023-02-27 23:07:43 -05:00
}
$str = $codedstr . $str ;
2021-07-14 08:11:18 -04:00
if ( defined $code ) {
2024-05-07 14:23:02 -04:00
$str .= "```$codelang\n$code\n```\n" ;
2021-07-14 08:11:18 -04:00
}
2021-03-16 13:46:03 -04:00
}
2021-07-14 08:11:18 -04:00
#print("\n\nWIKIFY CHUNK DONE:\n\n$str\n\n\n");
2021-03-16 13:46:03 -04:00
return $str ;
}
2021-07-14 08:11:18 -04:00
sub wikify {
2021-03-16 13:46:03 -04:00
my $wikitype = shift ;
my $str = shift ;
2021-07-14 08:11:18 -04:00
my $retval = '' ;
#print("WIKIFY WHOLE:\n\n$str\n\n\n");
2024-05-07 14:23:02 -04:00
while ( $str =~ s/\A(.*?)\`\`\`(.*?)\n(.*?)\n\`\`\`(\n|\Z)//ms ) {
2021-07-14 08:11:18 -04:00
$retval .= wikify_chunk ( $wikitype , $1 , $2 , $3 );
}
$retval .= wikify_chunk ( $wikitype , $str , undef , undef );
#print("WIKIFY WHOLE DONE:\n\n$retval\n\n\n");
return $retval ;
}
2026-06-18 13:42:44 -04:00
sub gen_manpage_url {
my $url = shift ;
my $desc = shift ;
# We have to unmangle some mangling we just did. :/
$url =~ s/\\\[char46\]/./g ;
# can't have newlines in this.
$desc =~ s/\n/ /g ;
return "\n.URL \"$url\" \"$desc\"\n" ;
}
2021-07-14 08:11:18 -04:00
2022-01-06 15:37:05 -05:00
my $dewikify_mode = 'md' ;
my $dewikify_manpage_code_indent = 1 ;
2021-07-14 08:11:18 -04: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-16 13:46:03 -04:00
2022-01-06 15:37:05 -05: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-16 13:46:03 -04:00
2022-06-15 23:25:36 -04:00
# Dump obvious wikilinks.
if ( defined $apiprefixregex ) {
$str =~ s/\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]/$1/gms ;
}
2021-03-16 13:46:03 -04:00
2022-05-25 10:42:11 -04:00
# links
$str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\[$2\]\($1\)/g ;
2022-01-06 15:37:05 -05:00
# <code></code> is also popular. :/
$str =~ s/\<code>(.*?)<\/code>/`$1`/gms ;
2021-03-16 13:46:03 -04:00
2022-01-06 15:37:05 -05:00
# bold+italic
$str =~ s/'''''(.*?)'''''/***$1***/gms ;
2021-03-16 13:46:03 -04:00
2022-01-06 15:37:05 -05:00
# bold
$str =~ s/'''(.*?)'''/**$1**/gms ;
2021-03-16 13:46:03 -04:00
2022-01-06 15:37:05 -05:00
# italic
$str =~ s/''(.*?)''/*$1*/gms ;
2021-03-24 10:45:27 -04:00
2022-01-06 15:37:05 -05:00
# bullets
$str =~ s/^\* /- /gm ;
2023-02-23 19:53:53 -05:00
} elsif ( $wikitype eq 'md' ) {
# Dump obvious wikilinks. The rest can just passthrough.
if ( defined $apiprefixregex ) {
2023-02-27 23:07:43 -05:00
$str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/$1/gms ;
2023-02-23 19:53:53 -05:00
}
2022-01-06 15:37:05 -05:00
}
2021-03-16 13:46:03 -04:00
2022-01-06 15:37:05 -05:00
if ( defined $code ) {
2024-05-07 14:23:02 -04:00
$str .= "\n```$codelang\n$code\n```\n" ;
2022-01-06 15:37:05 -05:00
}
} elsif ( $dewikify_mode eq 'manpage' ) {
2025-04-08 20:16:43 +02:00
# make sure these can't become part of roff syntax.
2025-08-30 19:39:24 +01:00
$str =~ s/\\/\\(rs/gms ;
2025-04-08 20:16:43 +02:00
$str =~ s/\./\\[char46]/gms ;
$str =~ s/"/\\(dq/gms ;
2025-05-14 11:34:42 -04:00
$str =~ s/'/\\(aq/gms ;
2025-04-08 20:16:43 +02:00
2022-01-06 15:37:05 -05:00
if ( $wikitype eq 'mediawiki' ) {
2022-06-15 23:25:36 -04:00
# Dump obvious wikilinks.
if ( defined $apiprefixregex ) {
$str =~ s/\s*\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]\s*/\n.BR $1\n/gms ;
}
2022-01-06 15:37:05 -05:00
2022-05-25 10:42:11 -04:00
# links
2026-06-18 13:42:44 -04:00
$str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]\s*/gen_manpage_url($1, $2)/ge ;
2022-05-25 10:42:11 -04:00
2022-01-06 15:37:05 -05:00
# <code></code> is also popular. :/
$str =~ s/\s*\<code>(.*?)<\/code>\s*/\n.BR $1\n/gms ;
2023-03-21 22:23:06 -07:00
# bold+italic (this looks bad, just make it bold).
$str =~ s/\s*'''''(.*?)'''''\s*/\n.B $1\n/gms ;
2022-01-06 15:37:05 -05: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 10:21:32 -05:00
} elsif ( $wikitype eq 'md' ) {
2025-04-08 20:16:43 +02:00
# bullets
$str =~ s/^\- /\n\\(bu /gm ;
# merge paragraphs
$str =~ s/^[ \t]+//gm ;
$str =~ s/([^\-\n])\n([^\-\n])/$1 $2/g ;
$str =~ s/\n\n/\n.PP\n/g ;
2023-02-24 10:21:32 -05:00
# Dump obvious wikilinks.
if ( defined $apiprefixregex ) {
2025-04-08 20:16:43 +02:00
my $apr = $apiprefixregex ;
if ( ! ( $apr =~ /\A\(.*\)\Z/s )) {
# we're relying on the apiprefixregex having a capturing group.
$apr = "(" . $apr . ")" ;
}
$str =~ s/(\S*?)\[\`?($apr[a-zA-Z0-9_]+)\`?\]\($apr[a-zA-Z0-9_]+\)(\S*)\s*/\n.BR "" "$1" "$2" "$5"\n/gm ;
# handle cases like "[x](x), [y](y), [z](z)" being separated.
while ( $str =~ s/(\.BR[^\n]*)\n\n\.BR/$1\n.BR/gm ) {}
2023-02-24 10:21:32 -05:00
}
# links
2026-06-18 13:42:44 -04:00
$str =~ s/\[([^\]]*?)]\((https?\:\/\/.*?)\)\s*/gen_manpage_url($2, $1)/ge ;
2023-02-24 10:21:32 -05:00
# <code></code> is also popular. :/
2025-04-08 20:16:43 +02:00
$str =~ s/\s*(\S*?)\`([^\n]*?)\`(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms ;
2023-02-24 10:21:32 -05:00
2023-03-21 22:23:06 -07:00
# bold+italic (this looks bad, just make it bold).
2025-04-08 20:16:43 +02:00
$str =~ s/\s*(\S*?)\*\*\*([^\n]*?)\*\*\*(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms ;
2023-02-24 10:21:32 -05:00
# bold
2025-04-08 20:16:43 +02:00
$str =~ s/\s*(\S*?)\*\*([^\n]*?)\*\*(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms ;
2023-02-24 10:21:32 -05:00
# italic
2025-04-08 20:16:43 +02:00
$str =~ s/\s*(\S*?)\*([^\n]*?)\*(\S*)\s*/\n.IR "" "$1" "$2" "$3"\n/gms ;
2022-01-06 15:37:05 -05:00
}
2025-04-08 20:16:43 +02:00
# cleanup unnecessary quotes
$str =~ s/(\.[IB]R?)(.*?) ""\n/$1$2\n/gm ;
$str =~ s/(\.[IB]R?) "" ""(.*?)\n/$1$2\n/gm ;
$str =~ s/"(\S+)"/$1/gm ;
# cleanup unnecessary whitespace
$str =~ s/ +\n/\n/gm ;
2022-01-06 15:37:05 -05:00
if ( defined $code ) {
$code =~ s/\A\n+//gms ;
$code =~ s/\n+\Z//gms ;
2025-04-08 20:16:43 +02:00
$code =~ s/\\/\\(rs/gms ;
2022-01-06 15:37:05 -05:00
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 02:03:44 -04: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-06 15:37:05 -05:00
} else {
2024-04-08 22:32:36 -04:00
die ( "Unexpected dewikify_mode" );
2021-07-14 08:11:18 -04:00
}
#print("\n\nDEWIKIFY CHUNK DONE:\n\n$str\n\n\n");
2021-03-16 13:46:03 -04:00
return $str ;
}
2021-07-14 08:11:18 -04: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 02:03:44 -04:00
if ( $wikitype eq 'mediawiki' ) {
while ( $str =~ s/\A(.*?)<syntaxhighlight lang='?(.*?)'?>(.*?)<\/syntaxhighlight\>//ms ) {
$retval .= dewikify_chunk ( $wikitype , $1 , $2 , $3 );
}
} elsif ( $wikitype eq 'md' ) {
2025-04-08 20:16:43 +02:00
while ( $str =~ s/\A(.*?)\n?```(.*?)\n(.*?)\n```\n//ms ) {
2024-05-07 14:23:02 -04:00
$retval .= dewikify_chunk ( $wikitype , $1 , $2 , $3 );
2024-05-05 02:03:44 -04:00
}
2021-07-14 08:11:18 -04:00
}
$retval .= dewikify_chunk ( $wikitype , $str , undef , undef );
#print("DEWIKIFY WHOLE DONE:\n\n$retval\n\n\n");
return $retval ;
}
2023-02-28 12:03:48 -05: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-16 13:46:03 -04:00
sub usage {
2023-03-18 00:25:44 +01: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-16 13:46:03 -04:00
}
usage () if not defined $srcpath ;
usage () if not defined $wikipath ;
#usage() if $copy_direction == 0;
2023-03-21 21:56:34 -07:00
if ( not defined $manpath ) {
$manpath = "$srcpath/man" ;
}
2021-03-16 13:46:03 -04:00
my @standard_wiki_sections = (
'Draft' ,
'[Brief]' ,
2021-10-23 14:37:47 -04:00
'Deprecated' ,
2024-03-31 12:56:00 -04:00
'Header File' ,
2021-03-16 13:46:03 -04:00
'Syntax' ,
'Function Parameters' ,
2024-04-08 22:34:11 -04:00
'Macro Parameters' ,
'Fields' ,
'Values' ,
2021-03-16 13:46:03 -04:00
'Return Value' ,
2021-03-24 10:46:05 -04:00
'Remarks' ,
2023-01-24 22:13:25 -05:00
'Thread Safety' ,
2021-03-16 13:46:03 -04:00
'Version' ,
2021-03-24 10:46:05 -04:00
'Code Examples' ,
2024-04-06 23:25:37 -04:00
'See Also'
2021-03-16 13:46:03 -04:00
);
2021-03-24 10:46:05 -04: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-03-31 12:56:00 -04:00
'Code Examples' , 1 ,
'Header File' , 1
2021-03-24 10:46:05 -04:00
);
2021-03-16 13:46:03 -04:00
my %headers = (); # $headers{"SDL_audio.h"} -> reference to an array of all lines of text in SDL_audio.h.
2024-04-06 02:10:42 -04:00
my %headersyms = (); # $headersyms{"SDL_OpenAudio"} -> string of header documentation for SDL_OpenAudio, with comment '*' bits stripped from the start. Newlines embedded!
2021-03-16 13:46:03 -04:00
my %headerdecls = ();
2024-04-06 02:10:42 -04: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 00:36:10 -04:00
my %headersymstype = (); # $headersymstype{"SDL_OpenAudio"} -> 1 (function), 2 (macro), 3 (struct), 4 (enum), 5 (other typedef)
2024-05-14 10:19:34 -04: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 10:44:37 -04:00
my %headercategorydocs = (); # $headercategorydocs{"Audio"} -> (fake) symbol for this category's documentation. Undefined if not documented.
2024-06-12 15:49:59 -04: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 02:10:42 -04: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-12-10 11:21:57 -05:00
my %quickreffuncorder = (); # contains references to array, each array item being a key to a category with functions in the order they appear in the headers, like $quickreffuncorder{"Audio"}[0] == 'SDL_GetNumAudioDrivers'
2024-04-06 02:10:42 -04:00
2024-04-30 22:47:02 -04: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-01 19:58:21 -04: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 02:10:42 -04: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-16 13:46:03 -04:00
2025-11-25 23:11:13 -05:00
# !!! FIXME: generalize this for other libraries to use.
2024-06-12 15:49:59 -04: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 ;
}
2024-12-10 11:21:57 -05:00
my %big_ascii = (
'A' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ],
'B' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'C' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ],
'D' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'E' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{255D}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ],
'F' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{255D}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}\x{20}\x{20}" ],
'G' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'H' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ],
'I' => [ "\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{255D}" ],
'J' => [ "\x{20}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{20}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'K' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ],
'L' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ],
'M' => [ "\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{255A}\x{2588}\x{2588}\x{2554}\x{255D}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{255A}\x{2550}\x{255D}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ],
'N' => [ "\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{255A}\x{2588}\x{2588}\x{2557}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{255D}" ],
'O' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'P' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}\x{20}\x{20}" ],
'Q' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{2584}\x{2584}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2580}\x{2580}\x{2550}\x{255D}\x{20}" ],
'R' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ],
'S' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'T' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{255D}" , "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}" ],
'U' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'V' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}" , "\x{20}\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}\x{20}" ],
'W' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}\x{2588}\x{2588}\x{2588}\x{2557}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{255D}\x{255A}\x{2550}\x{2550}\x{255D}\x{20}" ],
'X' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2588}\x{2588}\x{2557}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}" , "\x{20}\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ],
'Y' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2588}\x{2588}\x{2557}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}" , "\x{20}\x{20}\x{255A}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}" ],
'Z' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{20}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}" , "\x{20}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ],
' ' => [ "\x{20}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{20}" ],
'.' => [ "\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{255D}" ],
',' => [ "\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}" , "\x{2584}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{255D}" ],
'/' => [ "\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}" , "\x{20}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{255D}\x{20}\x{20}\x{20}" , "\x{255A}\x{2550}\x{255D}\x{20}\x{20}\x{20}\x{20}" ],
'!' => [ "\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{255D}" , "\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{255D}" ],
2024-12-17 23:23:13 -05:00
'_' => [ "\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}\x{20}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ],
2024-12-10 11:21:57 -05:00
'0' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{2588}\x{2588}\x{2554}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'1' => [ "\x{20}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{255A}\x{2550}\x{255D}" ],
'2' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ],
'3' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'4' => [ "\x{2588}\x{2588}\x{2557}\x{20}\x{20}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2551}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{20}\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}" ],
'5' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}" ],
'6' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" , "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'7' => [ "\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2554}\x{255D}\x{20}" , "\x{20}\x{20}\x{20}\x{2588}\x{2588}\x{2551}\x{20}\x{20}" , "\x{20}\x{20}\x{20}\x{255A}\x{2550}\x{255D}\x{20}\x{20}" ],
'8' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
'9' => [ "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2557}\x{20}" , "\x{2588}\x{2588}\x{2554}\x{2550}\x{2550}\x{2588}\x{2588}\x{2557}" , "\x{255A}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2588}\x{2588}\x{2551}" , "\x{20}\x{2588}\x{2588}\x{2588}\x{2588}\x{2588}\x{2554}\x{255D}" , "\x{20}\x{255A}\x{2550}\x{2550}\x{2550}\x{2550}\x{255D}\x{20}" ],
);
sub print_big_ascii_string {
my $fh = shift ;
my $str = shift ;
my $comment = shift ;
my $lowascii = shift ;
$comment = '' if not defined $comment ;
$lowascii = 0 if not defined $lowascii ;
my @chars = split // , $str ;
my $charcount = scalar ( @chars );
binmode ( $fh , ":utf8" );
my $maxrows = $lowascii ? 5 : 6 ;
for ( my $rownum = 0 ; $rownum < $maxrows ; $rownum ++ ){
print $fh $comment ;
my $charidx = 0 ;
foreach my $ch ( @chars ) {
my $rowsref = $big_ascii { uc ( $ch )};
die ( "Don't have a big ascii entry for '$ch'!\n" ) if not defined $rowsref ;
my $row = @$rowsref [ $rownum ];
2025-06-01 10:43:08 -04:00
my $outstr = '' ;
2024-12-10 11:21:57 -05:00
if ( $lowascii ) {
my @x = split // , $row ;
foreach ( @x ) {
2025-06-01 10:43:08 -04:00
$outstr .= ( $_ eq "\x{2588}" ) ? 'X' : ' ' ;
2024-12-10 11:21:57 -05:00
}
} else {
2025-06-01 10:43:08 -04:00
$outstr = $row ;
2024-12-10 11:21:57 -05:00
}
$charidx ++ ;
2025-06-01 10:43:08 -04:00
if ( $charidx == $charcount ) {
$outstr =~ s/\s*\Z// ; # dump extra spaces at the end of the line.
} else {
$outstr .= ' ' ; # space between glyphs.
2024-12-10 11:21:57 -05:00
}
2025-06-01 10:43:08 -04:00
print $fh $outstr ;
2024-12-10 11:21:57 -05:00
}
print $fh "\n" ;
}
}
sub generate_quickref {
my $briefsref = shift ;
my $path = shift ;
my $lowascii = shift ;
# !!! FIXME: this gitrev and majorver/etc stuff is copy/pasted a few times now.
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 ;
my $microver = 0 ;
while ( <FH> ) {
chomp ;
if ( /$versionmajorregex/ ) {
$majorver = int ( $1 );
} elsif ( /$versionminorregex/ ) {
$minorver = int ( $1 );
} elsif ( /$versionmicroregex/ ) {
$microver = int ( $1 );
}
}
close ( FH );
my $fullversion = "$majorver.$minorver.$microver" ;
my $tmppath = "$path.tmp" ;
open ( my $fh , '>' , $tmppath ) or die ( "Can't open '$tmppath': $!\n" );
if ( not @quickrefcategoryorder ) {
@quickrefcategoryorder = sort keys %headercategorydocs ;
}
#my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
#my $datestr = sprintf("%04d-%02d-%02d %02d:%02d:%02d GMT", $year+1900, $mon+1, $mday, $hour, $min, $sec);
print $fh "<!-- DO NOT EDIT THIS PAGE ON THE WIKI. IT WILL BE OVERWRITTEN BY WIKIHEADERS AND CHANGES WILL BE LOST! -->\n\n" ;
# Just something to test big_ascii output.
2024-12-17 23:23:13 -05:00
#print_big_ascii_string($fh, "ABCDEFGHIJ", '', $lowascii);
#print_big_ascii_string($fh, "KLMNOPQRST", '', $lowascii);
#print_big_ascii_string($fh, "UVWXYZ0123", '', $lowascii);
#print_big_ascii_string($fh, "456789JT3A", '', $lowascii);
#print_big_ascii_string($fh, "hello, _a.b/c_!!", '', $lowascii);
2024-12-10 11:21:57 -05:00
# Dan Bechard's work was on an SDL2 cheatsheet:
# https://blog.theprogrammingjunkie.com/post/sdl2-cheatsheet/
if ( $lowascii ) {
2024-12-10 11:36:19 -05:00
print $fh "# QuickReferenceNoUnicode\n\n" ;
2024-12-10 11:21:57 -05:00
print $fh "If you want to paste this into a text editor that can handle\n" ;
print $fh "fancy Unicode section headers, try using\n" ;
print $fh "[QuickReference](QuickReference) instead.\n\n" ;
} else {
2024-12-10 11:36:19 -05:00
print $fh "# QuickReference\n\n" ;
2024-12-10 11:21:57 -05:00
print $fh "If you want to paste this into a text editor that can't handle\n" ;
print $fh "the fancy Unicode section headers, try using\n" ;
print $fh "[QuickReferenceNoUnicode](QuickReferenceNoUnicode) instead.\n\n" ;
}
print $fh "```c\n" ;
print $fh "// $quickreftitle\n" if defined $quickreftitle ;
print $fh "//\n" ;
print $fh "// $quickrefurl\n//\n" if defined $quickrefurl ;
print $fh "// $quickrefdesc\n" if defined $quickrefdesc ;
#print $fh "// When this document was written: $datestr\n";
print $fh "// Based on $projectshortname version $fullversion\n" ;
#print $fh "// git revision $gitrev\n";
print $fh "//\n" ;
print $fh "// This can be useful in an IDE with search and syntax highlighting.\n" ;
print $fh "//\n" ;
print $fh "// Original idea for this document came from Dan Bechard (thanks!)\n" ;
print $fh "// ASCII art generated by: https://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow (with modified 'S' for readability)\n\n" ;
foreach ( @quickrefcategoryorder ) {
my $cat = $_ ;
my $maxlen = 0 ;
my @csigs = ();
my $funcorderref = $quickreffuncorder { $cat };
next if not defined $funcorderref ;
foreach ( @$funcorderref ) {
my $sym = $_ ;
my $csig = '' ;
if ( $headersymstype { $sym } == 1 ) { # function
$csig = "${headersymsrettype{$sym}} $sym" ;
my $fnsigparams = $headersymsparaminfo { $sym };
if ( not defined ( $fnsigparams )) {
$csig .= '(void);' ;
} else {
my $sep = '(' ;
for ( my $i = 0 ; $i < scalar ( @$fnsigparams ); $i += 2 ) {
my $paramname = @$fnsigparams [ $i ];
my $paramtype = @$fnsigparams [ $i + 1 ];
my $spc = ( $paramtype =~ /\*\Z/ ) ? '' : ' ' ;
$csig .= "$sep$paramtype$spc$paramname" ;
$sep = ', ' ;
}
$csig .= ");" ;
}
} elsif ( $headersymstype { $sym } == 2 ) { # macro
next if defined $quickrefmacroregex && not $sym =~ /$quickrefmacroregex/ ;
$csig = ( split /\n/ , $headerdecls { $sym })[ 0 ]; # get the first line from a multiline string.
if ( not $csig =~ s/\A(\#define [a-zA-Z0-9_]*\(.*?\))(\s+.*)?\Z/$1/ ) {
$csig =~ s/\A(\#define [a-zA-Z0-9_]*)(\s+.*)?\Z/$1/ ;
}
chomp ( $csig );
}
my $len = length ( $csig );
$maxlen = $len if $len > $maxlen ;
push @csigs , $sym ;
push @csigs , $csig ;
}
$maxlen += 2 ;
next if ( not @csigs );
print $fh "\n" ;
print_big_ascii_string ( $fh , $cat , '// ' , $lowascii );
print $fh "\n" ;
while ( @csigs ) {
my $sym = shift @csigs ;
my $csig = shift @csigs ;
my $brief = $$briefsref { $sym };
if ( defined $brief ) {
$brief = "$brief" ;
chomp ( $brief );
2024-12-13 13:52:35 -05:00
my $thiswikitype = defined $wikitypes { $sym } ? $wikitypes { $sym } : 'md' ; # default to MarkDown for new stuff.
$brief = dewikify ( $thiswikitype , $brief );
2024-12-10 11:21:57 -05:00
my $spaces = ' ' x ( $maxlen - length ( $csig ));
$brief = "$spaces// $brief" ;
} else {
$brief = '' ;
}
print $fh "$csig$brief\n" ;
}
}
print $fh "```\n\n" ;
close ( $fh );
# # Don't overwrite the file if nothing has changed besides the timestamp
# # and git revision.
# my $matches = 1;
# if ( not -f $path ) {
# $matches = 0; # always write if the file hasn't been created yet.
# } else {
# open(my $fh_a, '<', $tmppath) or die("Can't open '$tmppath': $!\n");
# open(my $fh_b, '<', $path) or die("Can't open '$path': $!\n");
# while (1) {
# my $a = <$fh_a>;
# my $b = <$fh_b>;
# $matches = 0, last if ((not defined $a) != (not defined $b));
# last if ((not defined $a) || (not defined $b));
# if ($a ne $b) {
# next if ($a =~ /\A\/\/ When this document was written:/);
# next if ($a =~ /\A\/\/ git revision /);
# $matches = 0;
# last;
# }
# }
#
# close($fh_a);
# close($fh_b);
# }
#
# if ($matches) {
# unlink($tmppath); # it's the same file except maybe the date/gitrev. Don't overwrite it.
# } else {
# rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n");
# }
rename ( $tmppath , $path ) or die ( "Can't rename '$tmppath' to '$path': $!\n" );
}
2025-09-02 15:49:52 -04:00
sub generate_envvar_wiki_page {
my $briefsref = shift ;
my $path = shift ;
return if not $envvarenabled or not defined $envvarsymregex or not defined $envvarsymreplace ;
my $replace = "\"$envvarsymreplace\"" ;
my $tmppath = "$path.tmp" ;
open ( my $fh , '>' , $tmppath ) or die ( "Can't open '$tmppath': $!\n" );
print $fh "<!-- DO NOT EDIT THIS PAGE ON THE WIKI. IT WILL BE OVERWRITTEN BY WIKIHEADERS AND CHANGES WILL BE LOST! -->\n\n" ;
print $fh "# $envvartitle\n\n" ;
if ( defined $envvardesc ) {
my $desc = "$envvardesc" ;
$desc =~ s/\\n/\n/g ; # replace "\n" strings with actual newlines.
print $fh "$desc\n\n" ;
}
print $fh "## Environment Variable List\n\n" ;
foreach ( sort keys %headersyms ) {
my $sym = $_ ;
next if $headersymstype { $sym } != 2 ; # not a #define? skip it.
my $hint = "$_" ;
next if not $hint =~ s/$envvarsymregex/$replace/ee ;
my $brief = $$briefsref { $sym };
if ( not defined $brief ) {
$brief = '' ;
} else {
$brief = "$brief" ;
chomp ( $brief );
my $thiswikitype = defined $wikitypes { $sym } ? $wikitypes { $sym } : 'md' ; # default to MarkDown for new stuff.
$brief = ": " . dewikify ( $thiswikitype , $brief );
}
print $fh "- [$hint]($sym)$brief\n" ;
}
print $fh "\n" ;
close ( $fh );
rename ( $tmppath , $path ) or die ( "Can't rename '$tmppath' to '$path': $!\n" );
}
2022-06-15 23:25:36 -04:00
my $incpath = "$srcpath" ;
$incpath .= "/$incsubdir" if $incsubdir ne '' ;
2023-02-28 11:37:46 -05:00
my $readmepath = undef ;
if ( defined $readmesubdir ) {
$readmepath = "$srcpath/$readmesubdir" ;
}
2021-03-16 13:46:03 -04:00
opendir ( DH , $incpath ) or die ( "Can't opendir '$incpath': $!\n" );
2023-03-24 13:57:24 -07:00
while ( my $d = readdir ( DH )) {
my $dent = $d ;
2022-06-15 23:25:36 -04:00
next if not $dent =~ /$selectheaderregex/ ; # just selected headers.
2021-03-16 13:46:03 -04:00
open ( FH , '<' , "$incpath/$dent" ) or die ( "Can't open '$incpath/$dent': $!\n" );
2024-05-14 10:19:34 -04: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-16 13:46:03 -04:00
my @contents = ();
2024-12-10 11:21:57 -05:00
my @function_order = ();
2024-04-13 22:55:23 -04:00
my $ignoring_lines = 0 ;
2024-05-01 19:58:21 -04:00
my $header_comment = - 1 ;
2024-05-16 10:44:37 -04:00
my $saw_category_doxygen = - 1 ;
2024-05-01 19:58:21 -04:00
my $lineno = 0 ;
2024-12-10 11:21:57 -05:00
2021-03-16 13:46:03 -04:00
while ( <FH> ) {
chomp ;
2024-05-01 19:58:21 -04:00
$lineno ++ ;
2024-04-06 02:10:42 -04:00
my $symtype = 0 ; # nothing, yet.
2021-10-08 14:39:28 -04:00
my $decl ;
my @templines ;
my $str ;
my $has_doxygen = 1 ;
2024-04-06 02:10:42 -04: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-01 19:58:21 -04:00
if ( $header_comment == - 1 ) {
$header_comment = /\A\/\*\s*\Z/ ? 1 : 0 ;
} elsif (( $header_comment == 1 ) && ( /\A\*\/\s*\Z/ )) {
$header_comment = 0 ;
}
2024-04-13 22:55:23 -04:00
if ( $ignoring_lines && /\A\s*\#\s*endif\s*\Z/ ) {
$ignoring_lines = 0 ;
push @contents , $_ ;
next ;
} elsif ( $ignoring_lines ) {
push @contents , $_ ;
next ;
2025-11-25 23:11:13 -05:00
} elsif ( /\A\s*\#\s*ifndef\s+$wikidocsectionsym\s*\Z/ ) {
2024-04-13 22:55:23 -04:00
$ignoring_lines = 1 ;
push @contents , $_ ;
next ;
2024-05-14 10:19:34 -04: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 ;
2025-11-25 23:11:13 -05:00
} elsif ( /\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym/ ) { # a function declaration without a doxygen comment?
2024-04-06 02:10:42 -04:00
$symtype = 1 ; # function declaration
2021-10-08 14:39:28 -04:00
@templines = ();
$decl = $_ ;
$str = '' ;
$has_doxygen = 0 ;
2025-11-25 23:11:13 -05:00
} elsif ( /\A\s*$forceinlinesym/ ) { # a (forced-inline) function declaration without a doxygen comment?
2024-04-13 20:08:14 -04:00
$symtype = 1 ; # function declaration
@templines = ();
$decl = $_ ;
$str = '' ;
$has_doxygen = 0 ;
2021-10-08 14:39:28 -04:00
} elsif ( not /\A\/\*\*\s*\Z/ ) { # not doxygen comment start?
2021-03-16 13:46:03 -04:00
push @contents , $_ ;
2024-05-01 19:58:21 -04:00
add_coverage_gap ( $_ , $dent , $lineno ) if ( $header_comment == 0 );
2021-03-16 13:46:03 -04:00
next ;
2021-10-08 14:39:28 -04:00
} else { # Start of a doxygen comment, parse it out.
2024-05-16 10:44:37 -04:00
my $is_category_doxygen = 0 ;
2021-10-08 14:39:28 -04:00
@templines = ( $_ );
while ( <FH> ) {
chomp ;
2024-05-01 19:58:21 -04:00
$lineno ++ ;
2021-10-08 14:39:28 -04: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-01 19:58:21 -04:00
$lineno ++ ;
2021-10-08 14:39:28 -04:00
push @templines , $_ ;
s/\A\s*\*\s?// ;
if ( s/\A\s*\`\`\`/```/ ) {
$str .= "$_\n" ;
last ;
} else {
$str .= "$_\n" ;
}
2021-07-14 08:11:18 -04:00
}
2021-10-08 14:39:28 -04:00
} else {
2024-05-16 10:44:37 -04: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-08 14:39:28 -04:00
$str .= "$_\n" ;
2021-07-14 08:11:18 -04:00
}
}
2021-03-16 13:46:03 -04:00
2024-05-16 10:44:37 -04:00
if ( $is_category_doxygen ) {
$str =~ s/\s*\Z// ;
$decl = '' ;
$symtype = - 1 ; # not a symbol at all.
2024-04-06 02:10:42 -04:00
} else {
2024-05-16 10:44:37 -04:00
$decl = <FH> ;
$lineno ++ if defined $decl ;
$decl = '' if not defined $decl ;
chomp ( $decl );
2025-11-25 23:11:13 -05:00
if ( $decl =~ /\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym/ ) {
2024-05-16 10:44:37 -04:00
$symtype = 1 ; # function declaration
2025-11-25 23:11:13 -05:00
} elsif ( $decl =~ /\A\s*$forceinlinesym/ ) {
2024-05-16 10:44:37 -04:00
$symtype = 1 ; # (forced-inline) function declaration
} elsif ( $decl =~ /\A\s*\#\s*define\s+/ ) {
$symtype = 2 ; # macro
2024-06-15 11:31:17 -04:00
} elsif ( $decl =~ /\A\s*(typedef\s+|)(struct|union)\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\Z)/ ) {
2024-05-16 10:44:37 -04:00
$symtype = 3 ; # struct or union
2024-06-15 11:31:17 -04:00
} elsif ( $decl =~ /\A\s*(typedef\s+|)enum\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\Z)/ ) {
2024-05-16 10:44:37 -04: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-08 14:39:28 -04:00
}
2021-03-16 13:46:03 -04:00
}
}
2024-06-12 15:49:59 -04:00
my @paraminfo = ();
my $rettype = undef ;
2021-03-16 13:46:03 -04:00
my @decllines = ( $decl );
2024-04-06 02:10:42 -04:00
my $sym = '' ;
2021-03-16 13:46:03 -04:00
2024-05-16 10:44:37 -04: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
2025-11-25 23:11:13 -05:00
my $is_forced_inline = ( $decl =~ /\A\s*$forceinlinesym/ );
2024-04-13 20:08:14 -04:00
if ( $is_forced_inline ) {
if ( not $decl =~ /\)\s*(\{.*|)\s*\Z/ ) {
while ( <FH> ) {
chomp ;
2024-05-01 19:58:21 -04:00
$lineno ++ ;
2024-04-13 20:08:14 -04:00
push @decllines , $_ ;
s/\A\s+// ;
s/\s+\Z// ;
$decl .= " $_" ;
last if /\)\s*(\{.*|)\s*\Z/ ;
}
2024-04-06 02:10:42 -04:00
}
2024-04-13 20:08:14 -04:00
$decl =~ s/\s*\)\s*(\{.*|)\s*\Z/);/ ;
} else {
2024-06-11 11:29:44 -04:00
if ( not $decl =~ /;/ ) {
2024-04-13 20:08:14 -04:00
while ( <FH> ) {
chomp ;
2024-05-01 19:58:21 -04:00
$lineno ++ ;
2024-04-13 20:08:14 -04:00
push @decllines , $_ ;
s/\A\s+// ;
s/\s+\Z// ;
$decl .= " $_" ;
2024-06-11 11:29:44 -04:00
last if /;/ ;
2024-04-13 20:08:14 -04:00
}
}
$decl =~ s/\s+\);\Z/);/ ;
2024-06-11 11:29:44 -04:00
$decl =~ s/\s+;\Z/;/ ;
2021-03-16 13:46:03 -04:00
}
2024-04-06 02:10:42 -04:00
$decl =~ s/\s+\Z// ;
2024-06-12 15:49:59 -04:00
$decl = strip_fn_declaration_metadata ( $decl );
2024-06-11 11:29:44 -04:00
2024-06-12 15:49:59 -04:00
my $paramsstr = undef ;
2025-11-25 23:11:13 -05:00
if ( ! $is_forced_inline && $decl =~ /\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym\w*\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*$callconvsym\s+(.*?)\s*\((.*?)\);/ ) {
2024-04-13 20:08:14 -04:00
$sym = $6 ;
2025-11-25 23:11:13 -05:00
$rettype = "$1$2$3$4$5" ;
2024-06-12 15:49:59 -04:00
$paramsstr = $7 ;
2025-11-25 23:11:13 -05:00
} elsif ( $is_forced_inline && $decl =~ /\A\s*$forceinlinesym\s+(?:$deprecatedsym\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/ ) {
$sym = $5 ;
$rettype = "$1$2$3$4" ;
$paramsstr = $6 ;
2024-06-12 15:49:59 -04:00
} else {
2024-04-06 02:10:42 -04:00
#print "Found doxygen but no function sig:\n$str\n\n";
foreach ( @templines ) {
push @contents , $_ ;
}
foreach ( @decllines ) {
push @contents , $_ ;
}
next ;
}
2024-06-12 15:49:59 -04: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 '...' ) {
2026-02-06 16:20:00 +01:00
die ( "Multiple '...' params?! ('$sym' in '$incpath/$dent')" ) if ( $dotdotdot );
2024-06-12 15:49:59 -04:00
$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 11:37:45 -04: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 06:05:57 -07:00
my $shrink_length = 0 ;
2024-06-11 11:37:45 -04:00
$decl = '' ; # rebuild this with the line breaks, since it looks better for syntax highlighting.
2024-04-13 20:08:14 -04:00
foreach ( @decllines ) {
if ( $decl eq '' ) {
2024-07-26 06:05:57 -07:00
my $temp ;
2024-04-13 20:08:14 -04:00
$decl = $_ ;
2024-07-26 06:05:57 -07:00
$temp = $decl ;
2025-11-25 23:11:13 -05:00
$temp =~ s/\Aextern\s+(?:$deprecatedsym\s+|)$declspecsym\w*\s+(.*?)\s+(\*?)$callconvsym\s+/$1$2 / ;
2024-07-26 06:05:57 -07:00
$shrink_length = length ( $decl ) - length ( $temp );
$decl = $temp ;
2024-04-13 20:08:14 -04:00
} else {
my $trimmed = $_ ;
2024-07-26 06:05:57 -07:00
$trimmed =~ s/\A\s{$shrink_length}// ; # shrink to match the removed "extern SDL_DECLSPEC SDLCALL "
2024-04-13 20:08:14 -04:00
$decl .= $trimmed ;
}
$decl .= "\n" ;
2024-04-06 02:10:42 -04:00
}
}
2024-05-02 12:49:30 -04:00
2024-06-12 15:49:59 -04:00
$decl = strip_fn_declaration_metadata ( $decl );
2024-06-11 10:58:02 -04:00
2024-05-02 12:49:30 -04: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-03 13:16:17 -04:00
# At Sam's request, don't list property defines with functions. (See #9440)
2025-06-15 22:32:15 -04:00
my $is_property = ( defined $apipropertyregex ) ? /$apipropertyregex/ : 0 ;
2024-05-03 13:16:17 -04:00
if ( ! $is_property ) {
if ( $blank_lines > 0 ) {
while ( $blank_lines > 0 ) {
$additional_decl .= "\n" ;
push @decllines , '' ;
$blank_lines -- ;
}
2024-05-02 12:49:30 -04:00
}
2024-05-03 13:16:17 -04:00
$additional_decl .= "\n$_" ;
push @decllines , $_ ;
$lastpos = tell ( FH );
2024-05-02 12:49:30 -04:00
}
} else {
seek ( FH , $lastpos , 0 ); # re-read eaten lines again next time.
$lineno = $lastlineno ;
last ;
}
}
$decl .= $additional_decl ;
2024-04-06 02:10:42 -04:00
} elsif ( $symtype == 2 ) { # a macro
2025-10-25 15:05:36 -04:00
if ( $decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)(\s+|\Z)/ ) {
2024-04-06 02:10:42 -04:00
$sym = $1 ;
} else {
#print "Found doxygen but no macro:\n$str\n\n";
foreach ( @templines ) {
push @contents , $_ ;
}
foreach ( @decllines ) {
push @contents , $_ ;
}
next ;
}
while ( $decl =~ /\\\Z/ ) {
my $l = <FH> ;
last if not $l ;
2024-05-01 19:58:21 -04:00
$lineno ++ ;
2024-04-06 02:10:42 -04:00
chomp ( $l );
push @decllines , $l ;
#$l =~ s/\A\s+//;
$l =~ s/\s+\Z// ;
$decl .= "\n$l" ;
}
2024-04-08 22:34:11 -04:00
} elsif (( $symtype == 3 ) || ( $symtype == 4 )) { # struct or union or enum
my $has_definition = 0 ;
2024-06-15 11:31:17 -04:00
if ( $decl =~ /\A\s*(typedef\s+|)(struct|union|enum)\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\;|\Z)/ ) {
2024-04-08 22:34:11 -04: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-16 23:45:14 -04:00
# It also "parses" enums enough to find out the elements of it.
2024-04-08 22:34:11 -04:00
if ( $has_definition ) {
my $started = 0 ;
my $brackets = 0 ;
my $pending = $decl ;
2024-08-16 23:45:14 -04:00
my $skipping_comment = 0 ;
2024-04-08 22:34:11 -04:00
$decl = '' ;
while ( ! $started || ( $brackets != 0 )) {
2024-08-16 23:45:14 -04:00
foreach my $seg ( split ( /([{}])/ , $pending )) { # (this will pick up brackets in comments! Be careful!)
2024-04-08 22:34:11 -04: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-16 23:45:14 -04: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-08 22:34:11 -04:00
if ( ! $started || ( $brackets != 0 )) {
$pending = <FH> ;
die ( "EOF/error reading $incpath/$dent while parsing $sym\n" ) if not $pending ;
2024-05-01 19:58:21 -04:00
$lineno ++ ;
2024-04-08 22:34:11 -04: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 00:36:10 -04:00
} elsif ( $symtype == 5 ) { # other typedef
2024-05-03 12:58:07 -04:00
if ( $decl =~ /\A\s*typedef\s+(.*)\Z/ ) {
2024-04-11 00:36:10 -04:00
my $tdstr = $1 ;
2024-05-03 12:58:07 -04: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 00:36:10 -04:00
#my $datatype;
2024-05-03 12:58:07 -04:00
if ( $tdstr =~ /\A(.*?)\s*\((.*?)\s*\*\s*(.*?)\)\s*\((.*?)(\))?/ ) { # a function pointer type
2024-04-11 00:36:10 -04: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 02:58:53 -04:00
2024-04-25 14:26:49 -04:00
# We assume any `#define`s directly after the typedef are related to it: probably bitflags for an integer typedef.
2024-04-25 16:46:43 -04:00
# We'll also allow some other basic preprocessor lines.
2024-04-25 14:26:49 -04:00
# Blank lines are allowed, anything else, even comments, are not.
my $blank_lines = 0 ;
2024-04-25 02:58:53 -04:00
my $lastpos = tell ( FH );
2024-05-01 19:58:21 -04:00
my $lastlineno = $lineno ;
2024-04-25 02:58:53 -04:00
my $additional_decl = '' ;
2024-05-02 12:49:30 -04:00
my $saw_define = 0 ;
2024-04-25 02:58:53 -04:00
while ( <FH> ) {
chomp ;
2024-04-25 14:26:49 -04:00
2024-05-01 19:58:21 -04:00
$lineno ++ ;
2024-04-25 14:26:49 -04:00
if ( /\A\s*\Z/ ) {
$blank_lines ++ ;
2024-04-30 22:47:02 -04: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-02 12:49:30 -04: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-04-30 22:47:02 -04: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-25 14:26:49 -04: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-01 19:58:21 -04:00
$lineno = $lastlineno ;
2024-04-25 02:58:53 -04:00
last ;
}
}
2024-04-25 14:26:49 -04:00
$decl .= $additional_decl ;
2024-04-06 02:10:42 -04:00
} else {
2024-04-08 22:32:36 -04:00
die ( "Unexpected symtype $symtype" );
2021-03-16 13:46:03 -04:00
}
#print("DECL: [$decl]\n");
2024-04-06 02:10:42 -04:00
#print("$sym:\n$str\n\n");
2021-07-14 08:11:18 -04:00
2021-10-08 20:49:51 -04: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 02:10:42 -04: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-08 20:49:51 -04:00
} else { # documented function already existed?
2024-04-06 02:10:42 -04:00
$skipsym = 1 ; # don't add this copy to the list of functions.
2021-10-08 20:49:51 -04:00
if ( $has_doxygen ) {
2024-04-06 02:10:42 -04:00
print STDERR "WARNING: Symbol '$sym' appears to be documented in multiple locations. Only keeping the first one we saw!\n" ;
2021-10-08 20:49:51 -04:00
}
2026-02-06 16:20:00 +01:00
push @contents , join ( "\n" , @decllines ) if ( scalar ( @decllines ) > 0 ); # just put the existing declaration in as-is.
2021-10-08 20:49:51 -04:00
}
}
2024-04-06 02:10:42 -04:00
if ( ! $skipsym ) {
2024-05-14 10:19:34 -04:00
$headersymscategory { $sym } = $current_wiki_category if defined $current_wiki_category ;
2024-04-06 02:10:42 -04:00
$headersyms { $sym } = $str ;
$headerdecls { $sym } = $decl ;
$headersymslocation { $sym } = $dent ;
$headersymschunk { $sym } = scalar ( @contents );
$headersymshasdoxygen { $sym } = $has_doxygen ;
$headersymstype { $sym } = $symtype ;
2024-06-12 15:49:59 -04:00
$headersymsparaminfo { $sym } = \ @paraminfo if ( scalar ( @paraminfo ) > 0 );
$headersymsrettype { $sym } = $rettype if ( defined ( $rettype ));
2024-12-10 11:21:57 -05:00
push @function_order , $sym if ( $symtype == 1 ) || ( $symtype == 2 );
2021-10-08 20:49:51 -04:00
push @contents , join ( "\n" , @templines );
2024-05-16 10:44:37 -04:00
push @contents , join ( "\n" , @decllines ) if ( scalar ( @decllines ) > 0 );
2021-10-08 20:49:51 -04:00
}
2021-03-16 13:46:03 -04:00
}
close ( FH );
$headers { $dent } = \ @contents ;
2024-12-10 11:21:57 -05:00
$quickreffuncorder { $current_wiki_category } = \ @function_order if defined $current_wiki_category ;
2021-03-16 13:46:03 -04:00
}
closedir ( DH );
2024-06-12 15:49:59 -04:00
2021-03-16 13:46:03 -04:00
opendir ( DH , $wikipath ) or die ( "Can't opendir '$wikipath': $!\n" );
2023-03-24 13:57:24 -07:00
while ( my $d = readdir ( DH )) {
my $dent = $d ;
2021-03-16 13:46:03 -04:00
my $type = '' ;
2022-06-15 23:25:36 -04:00
if ( $dent =~ /\.(md|mediawiki)\Z/ ) {
2021-03-16 13:46:03 -04:00
$type = $1 ;
} else {
next ; # only dealing with wiki pages.
}
2024-04-06 02:10:42 -04:00
my $sym = $dent ;
$sym =~ s/\..*\Z// ;
2022-06-23 16:07:35 -04:00
2024-05-14 10:19:34 -04:00
# (There are other pages to ignore, but these are known ones to not bother parsing.)
2022-06-23 16:07:35 -04:00
# Ignore FrontPage.
2024-04-06 02:10:42 -04:00
next if $sym eq 'FrontPage' ;
2022-06-23 16:07:35 -04:00
2021-03-16 13:46:03 -04:00
open ( FH , '<' , "$wikipath/$dent" ) or die ( "Can't open '$wikipath/$dent': $!\n" );
2024-05-16 10:44:37 -04: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-16 13:46:03 -04:00
my $current_section = '[start]' ;
my @section_order = ( $current_section );
my %sections = ();
$sections { $current_section } = '' ;
2022-06-17 14:39:50 -04:00
my $firstline = 1 ;
2021-03-16 13:46:03 -04:00
while ( <FH> ) {
chomp ;
my $orig = $_ ;
s/\A\s*// ;
s/\s*\Z// ;
if ( $type eq 'mediawiki' ) {
2022-06-17 14:39:50 -04:00
if ( defined ( $wikipreamble ) && $firstline && /\A\=\=\=\=\=\= (.*?) \=\=\=\=\=\=\Z/ && ( $1 eq $wikipreamble )) {
$firstline = 0 ; # skip this.
next ;
} elsif ( /\A\= (.*?) \=\Z/ ) {
$firstline = 0 ;
2024-04-06 02:10:42 -04:00
$current_section = ( $1 eq $sym ) ? '[Brief]' : $1 ;
2021-03-16 13:46:03 -04: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-17 14:39:50 -04:00
$firstline = 0 ;
2024-04-06 02:10:42 -04:00
$current_section = ( $1 eq $sym ) ? '[Brief]' : $1 ;
2021-03-16 13:46:03 -04: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-17 14:39:50 -04:00
$firstline = 0 ;
2021-03-16 13:46:03 -04: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-17 14:39:50 -04:00
if ( defined ( $wikipreamble ) && $firstline && /\A\#\#\#\#\#\# (.*?)\Z/ && ( $1 eq $wikipreamble )) {
$firstline = 0 ; # skip this.
next ;
} elsif ( /\A\#+ (.*?)\Z/ ) {
$firstline = 0 ;
2024-04-06 02:10:42 -04:00
$current_section = ( $1 eq $sym ) ? '[Brief]' : $1 ;
2021-03-16 13:46:03 -04: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-17 14:39:50 -04:00
$firstline = 0 ;
2021-03-16 13:46:03 -04: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-08 22:32:36 -04:00
die ( "Unexpected wiki file type. Fixme!" );
2021-03-16 13:46:03 -04:00
}
2022-06-17 14:39:50 -04:00
if ( $firstline ) {
$firstline = ( $_ ne '' );
}
if ( ! $firstline ) {
$sections { $current_section } .= "$orig\n" ;
}
2021-03-16 13:46:03 -04:00
}
close ( FH );
2021-03-24 12:52:48 -04:00
foreach ( keys %sections ) {
$sections { $_ } =~ s/\A\n+// ;
$sections { $_ } =~ s/\n+\Z// ;
$sections { $_ } .= "\n" ;
}
2024-04-06 23:25:37 -04: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-16 13:46:03 -04:00
if ( 0 ) {
foreach ( @section_order ) {
2024-04-06 02:10:42 -04:00
print ( "$sym SECTION '$_':\n" );
2021-03-16 13:46:03 -04:00
print ( $sections { $_ });
print ( "\n\n" );
}
}
2024-04-06 02:10:42 -04:00
$wikitypes { $sym } = $type ;
$wikisyms { $sym } = \ %sections ;
$wikisectionorder { $sym } = \ @section_order ;
2021-03-16 13:46:03 -04:00
}
closedir ( DH );
2024-04-06 02:10:42 -04:00
delete $wikisyms { "Undocumented" };
2023-09-06 20:28:23 +02:00
{
my $path = "$wikipath/Undocumented.md" ;
2024-04-06 02:10:42 -04:00
open ( my $fh , '>' , $path ) or die ( "Can't open '$path': $!\n" );
2023-09-06 20:28:23 +02:00
2024-04-06 02:10:42 -04:00
print $fh "# Undocumented\n\n" ;
print_undocumented_section ( $fh , 'Functions' , 1 );
2024-04-08 22:34:11 -04:00
#print_undocumented_section($fh, 'Macros', 2);
2023-09-06 20:28:23 +02:00
2024-04-06 02:10:42 -04:00
close ( $fh );
2023-09-06 20:28:23 +02:00
}
2021-03-16 13:46:03 -04:00
if ( $warn_about_missing ) {
2024-04-06 02:10:42 -04:00
foreach ( keys %wikisyms ) {
my $sym = $_ ;
if ( not defined $headersyms { $sym }) {
2024-06-12 15:49:59 -04:00
print STDERR "WARNING: $sym defined in the wiki but not the headers!\n" ;
2021-03-16 13:46:03 -04:00
}
}
2024-04-06 02:10:42 -04:00
foreach ( keys %headersyms ) {
my $sym = $_ ;
if ( not defined $wikisyms { $sym }) {
2024-06-12 15:49:59 -04:00
print STDERR "WARNING: $sym defined in the headers but not the wiki!\n" ;
2021-03-16 13:46:03 -04:00
}
}
}
if ( $copy_direction == 1 ) { # --copy-to-headers
my %changed_headers = ();
2021-03-25 12:50:18 -04:00
2022-01-06 15:37:05 -05:00
$dewikify_mode = 'md' ;
2021-03-25 12:50:18 -04:00
$wordwrap_mode = 'md' ; # the headers use Markdown format.
2024-04-06 02:10:42 -04:00
foreach ( keys %headersyms ) {
my $sym = $_ ;
next if not defined $wikisyms { $sym }; # don't have a page for that function, skip it.
2024-04-08 22:34:11 -04:00
my $symtype = $headersymstype { $sym };
2024-04-06 02:10:42 -04:00
my $wikitype = $wikitypes { $sym };
my $sectionsref = $wikisyms { $sym };
2023-03-24 13:57:24 -07:00
my $remarks = $sectionsref -> { 'Remarks' };
my $returns = $sectionsref -> { 'Return Value' };
my $threadsafety = $sectionsref -> { 'Thread Safety' };
my $version = $sectionsref -> { 'Version' };
2024-04-06 23:25:37 -04:00
my $related = $sectionsref -> { 'See Also' };
2023-03-24 13:57:24 -07:00
my $deprecated = $sectionsref -> { 'Deprecated' };
my $brief = $sectionsref -> { '[Brief]' };
2021-03-16 13:46:03 -04:00
my $addblank = 0 ;
my $str = '' ;
2024-04-08 22:34:11 -04:00
my $params = undef ;
my $paramstr = undef ;
2024-05-16 10:44:37 -04: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-08 22:34:11 -04: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 02:10:42 -04:00
$headersymshasdoxygen { $sym } = 1 ; # Added/changed doxygen for this header.
2021-10-08 14:39:28 -04:00
2021-03-16 13:46:03 -04: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-23 14:37:47 -04: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-16 13:46:03 -04: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-12 15:49:59 -04:00
my $c_datatype = shift @lines ;
2021-03-16 13:46:03 -04:00
my $name = shift @lines ;
my $desc = shift @lines ;
2024-06-12 15:49:59 -04: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-16 13:46:03 -04: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-12 15:49:59 -04:00
#print STDERR "SYM: $sym CDATATYPE: $c_datatype NAME: $name DESC: $desc TERM: $terminator\n";
2021-03-16 13:46:03 -04: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-08 22:34:11 -04:00
$str .= "$paramstr $name $firstline\n" ;
2021-03-16 13:46:03 -04:00
foreach ( @desclines ) {
$str .= "${whitespace}$_\n" ;
}
}
2023-02-23 19:53:53 -05:00
} elsif ( $wikitype eq 'md' ) {
my $l ;
$l = shift @lines ;
2024-06-12 15:49:59 -04:00
die ( "Unexpected data parsing Markdown table" ) if ( not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/ );
2023-02-23 19:53:53 -05:00
$l = shift @lines ;
2024-06-12 15:49:59 -04:00
die ( "Unexpected data parsing Markdown table" ) if ( not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/ );
2023-02-23 19:53:53 -05:00
while ( scalar ( @lines ) >= 1 ) {
$l = shift @lines ;
2024-06-12 15:49:59 -04: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-23 19:53:53 -05:00
} else {
last ; # we seem to have run out of table.
}
2024-06-12 15:49:59 -04: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-23 19:53:53 -05:00
}
2021-03-16 13:46:03 -04:00
} else {
die ( "write me" );
}
}
if ( defined $returns ) {
$str .= "\n" if $addblank ; $addblank = 1 ;
my $r = dewikify ( $wikitype , $returns );
2024-06-12 15:49:59 -04:00
$r =~ s/\A\(.*?\)\s*// ; # Chop datatype in parentheses off the front.
2021-03-16 13:46:03 -04:00
my $retstr = "\\returns" ;
2024-06-12 15:49:59 -04:00
if ( $r =~ s/\AReturn(s?)\s+// ) {
2021-03-16 13:46:03 -04: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-24 22:13:25 -05: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-16 13:46:03 -04: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 09:36:07 -04:00
s/\(\)\Z// ; # Convert "SDL_Func()" to "SDL_Func"
2022-06-19 00:15:03 -04:00
s/\[\[(.*?)\]\]/$1/ ; # in case some wikilinks remain.
2023-02-24 11:45:43 -05:00
s/\[(.*?)\]\(.*?\)/$1/ ; # in case some wikilinks remain.
2022-06-15 23:25:36 -04:00
s/\A\/*// ;
2024-05-07 14:23:02 -04:00
s/\A\s*[\:\*\-]\s*// ;
s/\A\s+// ;
s/\s+\Z// ;
2021-03-16 13:46:03 -04:00
$str .= "\\sa $_\n" ;
}
}
2024-04-06 02:10:42 -04:00
my $header = $headersymslocation { $sym };
2021-10-08 14:39:28 -04:00
my $contentsref = $headers { $header };
2024-04-06 02:10:42 -04:00
my $chunk = $headersymschunk { $sym };
2021-10-08 14:39:28 -04:00
2021-03-16 13:46:03 -04:00
my @lines = split /\n/ , $str ;
2021-10-08 14:39:28 -04:00
my $addnewline = (( $chunk > 0 ) && ( $$contentsref [ $chunk - 1 ] ne '' )) ? "\n" : '' ;
my $output = "$addnewline/**\n" ;
2021-03-16 13:46:03 -04:00
foreach ( @lines ) {
chomp ;
s/\s*\Z// ;
if ( $_ eq '' ) {
$output .= " *\n" ;
} else {
$output .= " * $_\n" ;
}
}
$output .= " */" ;
2024-05-16 10:44:37 -04:00
#print("$sym:\n[$output]\n\n");
2021-03-16 13:46:03 -04:00
$$contentsref [ $chunk ] = $output ;
2024-04-06 02:10:42 -04:00
#$$contentsref[$chunk+1] = $headerdecls{$sym};
2021-03-16 13:46:03 -04:00
$changed_headers { $header } = 1 ;
}
foreach ( keys %changed_headers ) {
2021-10-08 14:39:28 -04:00
my $header = $_ ;
# this is kinda inefficient, but oh well.
my @removelines = ();
2024-04-06 02:10:42 -04:00
foreach ( keys %headersymslocation ) {
my $sym = $_ ;
next if $headersymshasdoxygen { $sym };
next if $headersymslocation { $sym } ne $header ;
2021-10-08 14:39:28 -04: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 02:10:42 -04:00
push @removelines , $headersymschunk { $sym };
2021-10-08 14:39:28 -04:00
}
my $contentsref = $headers { $header };
foreach ( @removelines ) {
delete $$contentsref [ $_ ]; # delete DOES NOT RENUMBER existing elements!
}
my $path = "$incpath/$header.tmp" ;
2021-03-16 13:46:03 -04:00
open ( FH , '>' , $path ) or die ( "Can't open '$path': $!\n" );
foreach ( @$contentsref ) {
2021-10-08 14:39:28 -04:00
print FH "$_\n" if defined $_ ;
2021-03-16 13:46:03 -04:00
}
close ( FH );
2021-10-08 14:39:28 -04:00
rename ( $path , "$incpath/$header" ) or die ( "Can't rename '$path' to '$incpath/$header': $!\n" );
2021-03-16 13:46:03 -04:00
}
2023-02-28 11:37:46 -05:00
if ( defined $readmepath ) {
2025-05-05 23:36:08 -04:00
mkdir ( $readmepath ); # just in case
opendir ( DH , $wikipath ) or die ( "Can't opendir '$wikipath': $!\n" );
while ( readdir ( DH )) {
my $dent = $_ ;
2025-11-21 09:36:56 -05:00
if ( $dent =~ /\A(README|INTRO)\-.*?\.md\Z/ ) { # we only bridge Markdown files here that start with "README-" or "INTRO-".
2025-05-05 23:36:08 -04:00
filecopy ( "$wikipath/$dent" , "$readmepath/$dent" , "\n" );
2023-02-28 11:37:46 -05:00
}
}
2025-05-05 23:36:08 -04:00
closedir ( DH );
2023-02-28 11:37:46 -05:00
}
2024-05-16 10:44:37 -04:00
2021-03-16 13:46:03 -04:00
} elsif ( $copy_direction == - 1 ) { # --copy-to-wiki
2023-02-23 19:53:53 -05:00
2024-12-10 11:21:57 -05:00
my %briefs = (); # $briefs{'SDL_OpenAudio'} -> the \brief string for the function.
2023-02-23 19:53:53 -05:00
if ( defined $changeformat ) {
$dewikify_mode = $changeformat ;
$wordwrap_mode = $changeformat ;
}
2024-04-06 02:10:42 -04:00
foreach ( keys %headersyms ) {
my $sym = $_ ;
next if not $headersymshasdoxygen { $sym };
2024-05-16 10:44:37 -04:00
next if $sym =~ /\A\[category documentation\]/ ; # not real symbols, we handle this elsewhere.
2024-04-06 02:10:42 -04:00
my $symtype = $headersymstype { $sym };
my $origwikitype = defined $wikitypes { $sym } ? $wikitypes { $sym } : 'md' ; # default to MarkDown for new stuff.
2023-02-23 19:53:53 -05:00
my $wikitype = ( defined $changeformat ) ? $changeformat : $origwikitype ;
2024-04-08 22:32:36 -04:00
die ( "Unexpected wikitype '$wikitype'" ) if (( $wikitype ne 'mediawiki' ) and ( $wikitype ne 'md' ) and ( $wikitype ne 'manpage' ));
2021-03-16 13:46:03 -04:00
2024-04-06 02:10:42 -04:00
#print("$sym\n"); next;
2021-03-24 10:45:27 -04:00
$wordwrap_mode = $wikitype ;
2024-04-06 02:10:42 -04:00
my $raw = $headersyms { $sym }; # raw doxygen text with comment characters stripped from start/end and start of each line.
2021-10-08 14:39:28 -04:00
next if not defined $raw ;
2021-03-16 13:46:03 -04: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-06 23:25:02 -04:00
$brief =~ s/\s+\Z// ;
2021-03-16 13:46:03 -04: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 02:10:42 -04:00
next if not defined $briefsplit [ 0 ]; # No brief text? Probably a bogus Doxygen comment, skip it.
2021-03-24 10:45:27 -04:00
$brief = wikify ( $wikitype , shift @briefsplit ) . "\n" ;
2021-03-16 13:46:03 -04: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-08 22:34:11 -04:00
$remarks .= "$l\n" ;
2021-03-16 13:46:03 -04:00
}
2021-07-14 08:11:18 -04:00
#print("REMARKS:\n\n $remarks\n\n");
2021-03-16 13:46:03 -04:00
$remarks = wordwrap ( wikify ( $wikitype , $remarks ));
$remarks =~ s/\A\s*// ;
$remarks =~ s/\s*\Z// ;
2024-04-06 02:10:42 -04:00
my $decl = $headerdecls { $sym };
2021-03-16 13:46:03 -04:00
my $syntax = '' ;
if ( $wikitype eq 'mediawiki' ) {
$syntax = "<syntaxhighlight lang='c'>\n$decl</syntaxhighlight>\n" ;
} elsif ( $wikitype eq 'md' ) {
2024-06-11 12:37:41 -04:00
$decl =~ s/\n+\Z// ;
2021-03-16 13:46:03 -04:00
$syntax = "```c\n$decl\n```\n" ;
2024-04-08 22:32:36 -04:00
} else { die ( "Expected wikitype '$wikitype'" ); }
2021-03-16 13:46:03 -04: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-12-10 11:21:57 -05:00
$briefs { $sym } = $brief ;
2024-06-12 15:49:59 -04: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-13 16:45:59 -04:00
my $has_returns = 0 ;
2024-10-22 10:47:51 -04:00
my $has_threadsafety = 0 ;
2021-03-16 13:46:03 -04:00
while ( @doxygenlines ) {
my $l = shift @doxygenlines ;
2024-04-08 22:34:11 -04: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-16 13:46:03 -04: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 11:58:57 -04:00
if ( $subline eq '' ) { # empty line, make sure it keeps the newline char.
$desc .= "\n" ;
} else {
$desc .= " $subline" ;
}
2021-03-16 13:46:03 -04:00
}
2021-07-14 11:58:57 -04:00
$desc =~ s/[\s\n]+\Z//ms ;
2025-11-25 23:11:13 -05:00
if ( 0 ) {
if (( $desc =~ /\A[a-z]/ ) && ( not $desc =~ /$apiprefixregex/ )) {
print STDERR "WARNING: $sym\'s '\\param $arg' text starts with a lowercase letter: '$desc'. Fixing.\n" ;
$desc = ucfirst ( $desc );
2024-06-14 01:57:48 -04:00
}
}
if ( not $desc =~ /[\.\!]\Z/ ) {
print STDERR "WARNING: $sym\'s '\\param $arg' text doesn't end with punctuation: '$desc'. Fixing.\n" ;
$desc .= '.' ;
}
2024-06-12 15:49:59 -04: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-16 13:46:03 -04: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-12 15:49:59 -04:00
$params { $arg } = $desc ;
push @paramsorder , $arg ;
2021-03-16 13:46:03 -04:00
} elsif ( $l =~ /\A\\r(eturns?)\s+(.*)\Z/ ) {
2024-06-13 16:45:59 -04:00
$has_returns = 1 ;
2024-06-12 15:49:59 -04:00
# !!! FIXME: complain if this isn't a function or macro.
2021-03-16 13:46:03 -04:00
my $retstr = "R$1" ; # "Return" or "Returns"
my $desc = $2 ;
2024-06-13 18:10:28 -04:00
2021-03-16 13:46:03 -04: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 11:58:57 -04:00
if ( $subline eq '' ) { # empty line, make sure it keeps the newline char.
$desc .= "\n" ;
} else {
$desc .= " $subline" ;
}
2021-03-16 13:46:03 -04:00
}
2021-07-14 11:58:57 -04:00
$desc =~ s/[\s\n]+\Z//ms ;
2024-06-12 15:49:59 -04:00
2025-11-25 23:11:13 -05:00
if ( 0 ) {
if (( $desc =~ /\A[A-Z]/ ) && ( not $desc =~ /$apiprefixregex/ )) {
2024-06-14 01:57:48 -04:00
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-12 15:49:59 -04: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-23 14:37:47 -04: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-16 13:46:03 -04: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 11:58:57 -04:00
if ( $subline eq '' ) { # empty line, make sure it keeps the newline char.
$desc .= "\n" ;
} else {
$desc .= " $subline" ;
}
2021-03-16 13:46:03 -04:00
}
2021-07-14 11:58:57 -04:00
$desc =~ s/[\s\n]+\Z//ms ;
$sections { 'Version' } = wordwrap ( wikify ( $wikitype , $desc )) . "\n" ;
2023-01-24 22:13:25 -05: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 10:47:51 -04:00
$has_threadsafety = 1 ;
2021-03-16 13:46:03 -04:00
} elsif ( $l =~ /\A\\sa\s+(.*)\Z/ ) {
my $sa = $1 ;
2021-07-14 09:36:07 -04:00
$sa =~ s/\(\)\Z// ; # Convert "SDL_Func()" to "SDL_Func"
2024-04-06 23:25:37 -04:00
$sections { 'See Also' } = '' if not defined $sections { 'See Also' };
2021-03-16 13:46:03 -04:00
if ( $wikitype eq 'mediawiki' ) {
2024-04-06 23:25:37 -04:00
$sections { 'See Also' } .= ":[[$sa]]\n" ;
2021-03-16 13:46:03 -04:00
} elsif ( $wikitype eq 'md' ) {
2024-05-05 01:21:02 -04:00
$sections { 'See Also' } .= "- [$sa]($sa)\n" ;
2024-04-08 22:32:36 -04:00
} else { die ( "Expected wikitype '$wikitype'" ); }
2021-03-16 13:46:03 -04:00
}
}
2024-06-13 16:45:59 -04: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 10:47:51 -04: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-12 15:49:59 -04: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-03-31 12:56:00 -04:00
my $hfiletext = $wikiheaderfiletext ;
2024-04-06 02:10:42 -04:00
$hfiletext =~ s/\%fname\%/$headersymslocation{$sym}/g ;
2024-03-31 12:56:00 -04:00
$sections { 'Header File' } = "$hfiletext\n" ;
2021-03-24 10:45:27 -04:00
# Make sure this ends with a double-newline.
2024-04-06 23:25:37 -04:00
$sections { 'See Also' } .= "\n" if defined $sections { 'See Also' };
2021-03-24 10:45:27 -04:00
2024-04-11 13:34:29 -04: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!" );
}
2025-11-25 23:11:13 -05:00
my $str = "This $symtypename is available since $projectshortname 3.0.0." ;
2024-04-11 13:34:29 -04:00
$sections { 'Version' } = wordwrap ( wikify ( $wikitype , $str )) . "\n" ;
}
}
2021-03-16 13:46:03 -04:00
# We can build the wiki table now that we have all the data.
2024-06-12 15:49:59 -04:00
if ( scalar ( @final_params ) > 0 ) {
2021-03-16 13:46:03 -04:00
my $str = '' ;
if ( $wikitype eq 'mediawiki' ) {
2024-06-12 15:49:59 -04: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-16 13:46:03 -04:00
$str .= ( $str eq '' ) ? "{|\n" : "|-\n" ;
2024-06-12 15:49:59 -04:00
$str .= "|$c_datatype\n" if $params_has_c_datatype ;
2021-03-16 13:46:03 -04:00
$str .= "|'''$arg'''\n" ;
$str .= "|$desc\n" ;
}
$str .= "|}\n" ;
} elsif ( $wikitype eq 'md' ) {
my $longest_arg = 0 ;
2024-06-12 15:49:59 -04:00
my $longest_c_datatype = 0 ;
2021-03-16 13:46:03 -04:00
my $longest_desc = 0 ;
my $which = 0 ;
2024-06-12 15:49:59 -04:00
foreach ( @final_params ) {
2021-03-16 13:46:03 -04:00
if ( $which == 0 ) {
2024-06-12 15:49:59 -04:00
my $len = length ( $_ );
2021-03-16 13:46:03 -04:00
$longest_arg = $len if ( $len > $longest_arg );
$which = 1 ;
2024-06-12 15:49:59 -04:00
} elsif ( $which == 1 ) {
if ( defined ( $_ )) {
my $len = length ( wikify ( $wikitype , $_ ));
$longest_c_datatype = $len if ( $len > $longest_c_datatype );
}
$which = 2 ;
2021-03-16 13:46:03 -04:00
} else {
2021-03-24 10:45:27 -04:00
my $len = length ( wikify ( $wikitype , $_ ));
2021-03-16 13:46:03 -04:00
$longest_desc = $len if ( $len > $longest_desc );
$which = 0 ;
}
}
# Markdown tables are sort of obnoxious.
2024-06-12 15:49:59 -04: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" ;
2021-03-16 13:46:03 -04:00
2024-06-12 15:49:59 -04:00
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-16 13:46:03 -04:00
}
} else {
2024-04-08 22:32:36 -04:00
die ( "Unexpected wikitype!" ); # should have checked this elsewhere.
2021-03-16 13:46:03 -04:00
}
$sections { 'Function Parameters' } = $str ;
}
2024-05-16 10:44:37 -04:00
my $path = "$wikipath/$sym.${wikitype}.tmp" ;
2021-03-16 13:46:03 -04:00
open ( FH , '>' , $path ) or die ( "Can't open '$path': $!\n" );
2024-04-06 02:10:42 -04:00
my $sectionsref = $wikisyms { $sym };
2021-03-16 13:46:03 -04:00
foreach ( @standard_wiki_sections ) {
# drop sections we either replaced or removed from the original wiki's contents.
2021-03-24 10:46:05 -04:00
if ( not defined $only_wiki_sections { $_ }) {
delete ( $$sectionsref { $_ });
}
2021-03-16 13:46:03 -04:00
}
2024-04-06 02:10:42 -04:00
my $wikisectionorderref = $wikisectionorder { $sym };
2021-03-16 13:46:03 -04:00
2021-09-01 00:19:54 -04: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-23 19:53:53 -05: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 10:44:37 -04:00
if ( $symtype != - 1 ) { # Don't do these in category documentation block
my $footer = $$sectionsref { '[footer]' };
2023-02-23 19:53:53 -05:00
2024-05-16 10:44:37 -04:00
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!" );
}
2024-04-06 02:10:42 -04:00
2024-05-16 10:44:37 -04:00
my $symcategory = $headersymscategory { $sym };
2022-06-17 14:39:50 -04:00
if ( $wikitype eq 'mediawiki' ) {
2024-05-16 10:44:37 -04: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-17 14:39:50 -04:00
} elsif ( $wikitype eq 'md' ) {
2024-05-16 10:44:37 -04: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-08 22:32:36 -04:00
} else { die ( "Unexpected wikitype '$wikitype'" ); }
2024-05-16 10:44:37 -04: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-17 14:39:50 -04:00
}
2021-09-01 00:19:54 -04:00
my $prevsectstr = '' ;
my @ordered_sections = ( @standard_wiki_sections , defined $wikisectionorderref ? @$wikisectionorderref : ()); # this copies the arrays into one.
2021-03-16 13:46:03 -04: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-08 22:34:11 -04:00
2021-03-16 13:46:03 -04:00
if ( $sect eq '[footer]' ) {
2021-09-01 00:19:54 -04: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-16 13:46:03 -04:00
print FH "----\n" ; # It's the same in Markdown and MediaWiki.
} elsif ( $sect eq '[Brief]' ) {
if ( $wikitype eq 'mediawiki' ) {
2024-04-06 02:10:42 -04:00
print FH "= $sym =\n\n" ;
2021-03-16 13:46:03 -04:00
} elsif ( $wikitype eq 'md' ) {
2024-04-06 02:10:42 -04:00
print FH "# $sym\n\n" ;
2024-04-08 22:32:36 -04:00
} else { die ( "Unexpected wikitype '$wikitype'" ); }
2021-03-16 13:46:03 -04:00
} else {
2024-04-08 22:34:11 -04: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 00:36:10 -04:00
if (( $symtype == 1 ) || ( $symtype == 5 )) { # function (or typedef, in case it's a function pointer type).
2024-04-08 22:34:11 -04: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 10:44:37 -04: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-16 13:46:03 -04:00
}
2021-09-01 00:19:54 -04:00
my $sectstr = defined $sections { $sect } ? $sections { $sect } : $$sectionsref { $sect };
print FH $sectstr ;
$prevsectstr = $sectstr ;
2021-03-16 13:46:03 -04:00
# make sure these don't show up twice.
delete ( $sections { $sect });
delete ( $$sectionsref { $sect });
}
print FH "\n\n" ;
close ( FH );
2023-02-23 19:53:53 -05:00
if ( defined $changeformat and ( $origwikitype ne $wikitype )) {
system ( "cd '$wikipath' ; git mv '$_.${origwikitype}' '$_.${wikitype}'" );
unlink ( "$wikipath/$_.${origwikitype}" );
}
2021-03-16 13:46:03 -04:00
rename ( $path , "$wikipath/$_.${wikitype}" ) or die ( "Can't rename '$path' to '$wikipath/$_.${wikitype}': $!\n" );
}
2022-01-06 15:37:05 -05:00
2024-04-30 22:47:02 -04: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-16 23:45:14 -04:00
my $category = 'CategoryAPIMacro' ;
if ( $headersymstype { $refersto } == 4 ) {
$category = 'CategoryAPIEnumerators' ; # NOT CategoryAPIEnum!
}
2024-04-30 22:47:02 -04:00
print FH "# $sym\n\nPlease refer to [$refersto]($refersto) for details.\n\n" ;
2024-05-14 10:19:34 -04:00
print FH "----\n" ;
2024-08-16 23:45:14 -04:00
print FH "[CategoryAPI](CategoryAPI), [$category]($category)\n\n" ;
2024-04-30 22:47:02 -04:00
close ( FH );
}
2024-05-16 10:44:37 -04: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-02-28 11:37:46 -05:00
if ( defined $readmepath ) {
if ( - d $readmepath ) {
2025-05-05 23:36:08 -04:00
mkdir ( $wikipath ); # just in case
2023-02-28 11:37:46 -05:00
opendir ( DH , $readmepath ) or die ( "Can't opendir '$readmepath': $!\n" );
2023-03-24 13:57:24 -07:00
while ( my $d = readdir ( DH )) {
my $dent = $d ;
2025-11-21 09:36:56 -05:00
if ( $dent =~ /\A(README|INTRO)\-.*?\.md\Z/ ) { # we only bridge Markdown files here that start with "README-" or "INTRO".
2025-05-05 23:36:08 -04:00
filecopy ( "$readmepath/$dent" , "$wikipath/$dent" , "\n" );
2023-02-28 11:37:46 -05:00
}
}
closedir ( DH );
2023-02-28 12:26:31 -05:00
my @pages = ();
2025-05-05 23:36:08 -04:00
opendir ( DH , $wikipath ) or die ( "Can't opendir '$wikipath': $!\n" );
2023-03-24 13:57:24 -07:00
while ( my $d = readdir ( DH )) {
my $dent = $d ;
2025-11-21 09:36:56 -05:00
if ( $dent =~ /\A((README|INTRO)\-.*?)\.md\Z/ ) {
2025-05-05 23:56:12 -04:00
push @pages , $1 ;
2023-02-28 11:37:46 -05:00
}
}
closedir ( DH );
2023-02-28 12:26:31 -05:00
2025-05-05 23:49:06 -04:00
open ( FH , '>' , "$wikipath/READMEs.md" ) or die ( "Can't open '$wikipath/READMEs.md': $!\n" );
2023-02-28 12:26:31 -05:00
print FH "# All READMEs available here\n\n" ;
foreach ( sort @pages ) {
my $wikiname = $_ ;
print FH "- [$wikiname]($wikiname)\n" ;
}
2023-02-28 11:37:46 -05:00
close ( FH );
}
}
2024-12-10 11:21:57 -05:00
# Write out quick reference pages...
if ( $quickrefenabled ) {
generate_quickref ( \ %briefs , "$wikipath/QuickReference.md" , 0 );
generate_quickref ( \ %briefs , "$wikipath/QuickReferenceNoUnicode.md" , 1 );
}
2025-09-02 15:49:52 -04:00
if ( $envvarenabled and defined $envvarsymregex and defined $envvarsymreplace ) {
generate_envvar_wiki_page ( \ %briefs , "$wikipath/EnvironmentVariables.md" );
}
2022-01-06 15:37:05 -05: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 01:03:07 -04:00
File::Path:: make_path ( "$manpath/man3" );
2022-01-06 15:37:05 -05: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-04 18:56:00 +02:00
if ( ! $gitrev ) {
$gitrev = `cd "$srcpath" ; git rev-list HEAD~..` ;
chomp ( $gitrev );
}
2022-01-06 15:37:05 -05:00
2022-06-15 23:25:36 -04:00
# !!! FIXME
open ( FH , '<' , "$srcpath/$versionfname" ) or die ( "Can't open '$srcpath/$versionfname': $!\n" );
2022-01-06 15:37:05 -05:00
my $majorver = 0 ;
my $minorver = 0 ;
2024-05-14 07:47:13 -07:00
my $microver = 0 ;
2022-01-06 15:37:05 -05:00
while ( <FH> ) {
chomp ;
2022-06-15 23:25:36 -04:00
if ( /$versionmajorregex/ ) {
2022-01-06 15:37:05 -05:00
$majorver = int ( $1 );
2022-06-15 23:25:36 -04:00
} elsif ( /$versionminorregex/ ) {
2022-01-06 15:37:05 -05:00
$minorver = int ( $1 );
2024-05-14 07:47:13 -07:00
} elsif ( /$versionmicroregex/ ) {
$microver = int ( $1 );
2022-01-06 15:37:05 -05:00
}
}
close ( FH );
2024-05-14 07:47:13 -07:00
my $fullversion = "$majorver.$minorver.$microver" ;
2022-01-06 15:37:05 -05:00
2024-04-06 02:10:42 -04: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 10:44:37 -04:00
next if $sym =~ /\A\[category documentation\]/ ; # not real symbols
2025-01-09 14:12:07 -05:00
next if ( defined $manpagesymbolfilterregex ) && ( $sym =~ /$manpagesymbolfilterregex/ );
2024-04-08 22:34:11 -04:00
my $symtype = $headersymstype { $sym };
2024-04-06 02:10:42 -04:00
my $wikitype = $wikitypes { $sym };
my $sectionsref = $wikisyms { $sym };
2023-03-24 13:57:24 -07:00
my $remarks = $sectionsref -> { 'Remarks' };
my $returns = $sectionsref -> { 'Return Value' };
my $version = $sectionsref -> { 'Version' };
my $threadsafety = $sectionsref -> { 'Thread Safety' };
2024-04-06 23:25:37 -04:00
my $related = $sectionsref -> { 'See Also' };
2023-03-24 13:57:24 -07:00
my $examples = $sectionsref -> { 'Code Examples' };
my $deprecated = $sectionsref -> { 'Deprecated' };
2024-03-31 12:56:00 -04:00
my $headerfile = $manpageheaderfiletext ;
2025-04-08 20:16:43 +02:00
my $params = undef ;
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.
$params = $sectionsref -> { 'Function Parameters' };
} elsif ( $symtype == 2 ) {
$params = $sectionsref -> { 'Macro Parameters' };
} elsif ( $symtype == 3 ) {
$params = $sectionsref -> { 'Fields' };
} elsif ( $symtype == 4 ) {
$params = $sectionsref -> { 'Values' };
} else {
die ( "Unexpected symtype $symtype" );
}
2024-04-06 02:10:42 -04:00
$headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g ;
2024-03-31 12:56:00 -04:00
$headerfile .= "\n" ;
2024-04-11 01:03:07 -04: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-24 13:57:24 -07:00
my $brief = $sectionsref -> { '[Brief]' };
2024-04-06 02:10:42 -04:00
my $decl = $headerdecls { $sym };
2022-01-06 15:37:05 -05:00
my $str = '' ;
2025-01-13 15:17:52 -05:00
# the "$brief" makes sure this is a copy of the string, which is doing some weird reference thing otherwise.
$brief = defined $brief ? "$brief" : '' ;
2022-01-06 15:37:05 -05:00
$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 );
2026-04-15 13:28:52 -04:00
# Hack: `apropros` doesn't like escaped character things like `\[char46]` for `.`...since almost every
# manpage will end their Brief section with a period and it won't wordwrap to risk being a groff control
# character, just replace it.
$brief =~ s/\\\[char46\]/./g ;
2022-01-06 15:37:05 -05:00
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 02:10:42 -04:00
$str .= ".\\\" This manpage was generated from ${projectshortname}'s wiki page for $sym:\n" ;
$str .= ".\\\" $wikiurl/$sym\n" ;
2022-01-06 15:37:05 -05: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-15 23:25:36 -04:00
$str .= ".\\\" $bugreporturl\n" ;
2022-01-06 15:37:05 -05:00
$str .= ".\\\" Please report issues in the generation of this manpage from the wiki at:\n" ;
2025-11-25 23:11:13 -05:00
$str .= ".\\\" https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n" ; # !!! FIXME: if this becomes a problem for other projects, we'll generalize this.
2022-06-15 23:25:36 -04:00
$str .= ".\\\" $projectshortname can be found at $projecturl\n" ;
2022-01-06 15:37:05 -05:00
2022-05-25 10:42:11 -04: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 01:03:07 -04:00
$str .= ".TH $sym $mansection \"$projectshortname $fullversion\" \"$projectfullname\" \"$projectshortname$majorver $mansectionname\"\n" ;
2022-01-06 15:37:05 -05:00
$str .= ".SH NAME\n" ;
2024-04-06 02:10:42 -04:00
$str .= "$sym" ;
2022-01-06 15:37:05 -05:00
$str .= " \\- $brief" if ( defined $brief );
$str .= "\n" ;
2024-03-31 12:56:00 -04:00
if ( defined $deprecated ) {
$str .= ".SH DEPRECATED\n" ;
$str .= dewikify ( $wikitype , $deprecated ) . "\n" ;
}
2025-04-08 20:16:43 +02:00
my $incfile = $mainincludefname ;
2024-03-31 12:56:00 -04:00
if ( defined $headerfile ) {
2025-04-08 20:16:43 +02:00
if ( $headerfile =~ /Defined in (.*)/ ) {
$incfile = $1 ;
}
2024-03-31 12:56:00 -04:00
}
2022-01-06 15:37:05 -05:00
$str .= ".SH SYNOPSIS\n" ;
$str .= ".nf\n" ;
2025-04-08 20:16:43 +02:00
$str .= ".B #include <$incfile>\n" ;
2022-01-06 15:37:05 -05:00
$str .= ".PP\n" ;
my @decllines = split /\n/ , $decl ;
foreach ( @decllines ) {
2025-04-08 20:16:43 +02:00
$_ =~ s/\\/\\(rs/g ; # fix multiline macro defs
$_ =~ s/"/\\(dq/g ;
2022-01-06 15:37:05 -05:00
$str .= ".BI \"$_\n" ;
}
$str .= ".fi\n" ;
if ( defined $remarks ) {
$str .= ".SH DESCRIPTION\n" ;
$str .= $remarks . "\n" ;
}
if ( defined $params ) {
2024-04-11 00:36:10 -04:00
if (( $symtype == 1 ) || ( $symtype == 5 )) {
2024-04-08 22:34:11 -04: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-06 15:37:05 -05: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-12 15:49:59 -04:00
my $c_datatype = shift @lines ;
2022-01-06 15:37:05 -05:00
my $name = shift @lines ;
my $desc = shift @lines ;
2024-06-12 15:49:59 -04: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-06 15:37:05 -05: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-12 15:49:59 -04:00
#print STDERR "SYM: $sym CDATATYPE: $c_datatype NAME: $name DESC: $desc TERM: $terminator\n";
2022-01-06 15:37:05 -05:00
$str .= ".TP\n" ;
$str .= ".I $name\n" ;
$str .= "$desc\n" ;
}
2023-02-24 10:21:32 -05:00
} elsif ( $wikitype eq 'md' ) {
my $l ;
$l = shift @lines ;
2024-06-12 15:49:59 -04:00
die ( "Unexpected data parsing Markdown table" ) if ( not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/ );
2023-02-24 10:21:32 -05:00
$l = shift @lines ;
2024-06-12 15:49:59 -04:00
die ( "Unexpected data parsing Markdown table" ) if ( not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/ );
2023-02-24 10:21:32 -05:00
while ( scalar ( @lines ) >= 1 ) {
$l = shift @lines ;
2024-06-12 15:49:59 -04: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 10:21:32 -05:00
} else {
last ; # we seem to have run out of table.
}
2024-06-12 15:49:59 -04: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 10:21:32 -05:00
}
2022-01-06 15:37:05 -05:00
} else {
die ( "write me" );
}
}
if ( defined $returns ) {
2025-05-20 14:25:41 +02:00
# Check for md link in return type: ([SDL_Renderer](SDL_Renderer) *)
# This would've prevented the next regex from working properly (it'd leave " *)")
2026-05-29 21:10:37 -04:00
$returns =~ s/\A\((const|)\s*\[.*?\]\((.*?)\)/\($2/ms ;
2025-04-08 20:16:43 +02:00
# Chop datatype in parentheses off the front.
2025-05-20 14:25:41 +02:00
$returns =~ s/\A\(.*?\) // ;
2024-06-12 15:49:59 -04:00
$returns = dewikify ( $wikitype , $returns );
2022-01-06 15:37:05 -05:00
$str .= ".SH RETURN VALUE\n" ;
2024-06-12 15:49:59 -04:00
$str .= "$returns\n" ;
2022-01-06 15:37:05 -05: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-24 22:13:25 -05:00
if ( defined $threadsafety ) {
$str .= ".SH THREAD SAFETY\n" ;
$str .= dewikify ( $wikitype , $threadsafety ) . "\n" ;
}
2022-01-06 15:37:05 -05: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 00:15:03 -04:00
s/\[\[(.*?)\]\]/$1/ ; # in case some wikilinks remain.
2023-02-24 11:45:43 -05:00
s/\[(.*?)\]\(.*?\)/$1/ ; # in case some wikilinks remain.
2023-02-24 10:21:32 -05:00
s/\A\*\s*\Z// ;
2022-06-15 23:25:36 -04:00
s/\A\/*// ;
2022-01-06 15:37:05 -05:00
s/\A\.BR\s+// ; # dewikify added this, but we want to handle it.
2023-02-24 10:21:32 -05:00
s/\A\.I\s+// ; # dewikify added this, but we want to handle it.
2025-04-08 20:16:43 +02:00
s/\A\.PP\s*// ; # dewikify added this, but we want to handle it.
s/\\\(bu// ; # dewikify added this, but we want to handle it.
2024-05-07 14:23:02 -04:00
s/\A\s*[\:\*\-]\s*// ;
2022-01-06 15:37:05 -05:00
s/\A\s+// ;
s/\s+\Z// ;
next if $_ eq '' ;
2024-04-30 21:35:35 -04: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-06 15:37:05 -05: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 02:10:42 -04:00
$str .= ".UR $wikiurl/$sym\n" ;
2022-06-15 23:25:36 -04:00
$str .= "${projectshortname}'s wiki\n" ;
2022-01-06 15:37:05 -05: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-15 23:25:36 -04:00
$str .= ".UR $bugreporturl\n" ;
2022-01-06 15:37:05 -05:00
$str .= "our bugtracker!\n" ;
$str .= ".UE\n" ;
}
2024-04-30 21:21:04 -04:00
my $path = "$manpath/man3/$_.$mansection" ;
2024-04-11 00:36:10 -04:00
my $tmppath = "$path.tmp" ;
open ( FH , '>' , $tmppath ) or die ( "Can't open '$tmppath': $!\n" );
2022-01-06 15:37:05 -05:00
print FH $str ;
close ( FH );
2024-04-11 00:36:10 -04:00
rename ( $tmppath , $path ) or die ( "Can't rename '$tmppath' to '$path': $!\n" );
2022-01-06 15:37:05 -05:00
}
2024-05-05 02:03:44 -04: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 07:47:13 -07:00
my $microver = 0 ;
2024-05-05 02:03:44 -04:00
while ( <FH> ) {
chomp ;
if ( /$versionmajorregex/ ) {
$majorver = int ( $1 );
} elsif ( /$versionminorregex/ ) {
$minorver = int ( $1 );
2024-05-14 07:47:13 -07:00
} elsif ( /$versionmicroregex/ ) {
$microver = int ( $1 );
2024-05-05 02:03:44 -04:00
}
}
close ( FH );
2024-05-14 07:47:13 -07:00
my $fullversion = "$majorver.$minorver.$microver" ;
2024-05-05 02:03:44 -04: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=3mm,
belowskip=3mm,
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 07:47:13 -07:00
\\title{$projectfullname $majorver.$minorver.$microver Reference Manual}
2024-05-05 02:03:44 -04: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 02:44:45 -04: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 02:03:44 -04:00
if ( $rc == 0 ) {
2024-05-05 02:44:45 -04:00
$rc = lc ( $a ) cmp lc ( $b );
2024-05-05 02:03:44 -04:00
}
return $rc ;
} keys %headersyms ;
my $current_symtype = 0 ;
2024-05-05 02:44:45 -04:00
my $current_chapter = '' ;
2024-05-05 02:03:44 -04:00
foreach ( @headersymskeys ) {
my $sym = $_ ;
next if not defined $wikisyms { $sym }; # don't have a page for that function, skip it.
2024-05-16 10:44:37 -04:00
next if $sym =~ /\A\[category documentation\]/ ; # not real symbols.
2024-05-05 02:03:44 -04: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 02:44:45 -04:00
if ( $current_chapter ne $newchapter ) {
$str .= "\n\n\\chapter{$projectshortname $newchapter}\n\n\\clearpage\n\n" ;
$current_chapter = $newchapter ;
}
2024-05-05 02:03:44 -04: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-12 15:49:59 -04:00
# !!! FIXME: this table parsing has gotten complicated and is pasted three times in this file; move it to a subroutine!
2024-05-05 02:03:44 -04: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-12 15:49:59 -04:00
die ( "Unexpected data parsing Markdown table" ) if ( not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/ );
2024-05-05 02:03:44 -04:00
$l = shift @lines ;
2024-06-12 15:49:59 -04:00
die ( "Unexpected data parsing Markdown table" ) if ( not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/ );
2024-05-05 02:03:44 -04:00
while ( scalar ( @lines ) >= 1 ) {
$l = shift @lines ;
2024-06-12 15:49:59 -04: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 02:03:44 -04:00
} else {
last ; # we seem to have run out of table.
}
2024-06-12 15:49:59 -04: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 02:03:44 -04:00
}
} else {
die ( "write me" );
}
$str .= " \\end{tabular}\n" ;
$str .= "\\end{center}\n" ;
}
if ( defined $returns ) {
2024-06-12 15:49:59 -04:00
$returns = dewikify ( $wikitype , $returns );
$returns =~ s/\A\(.*?\)\s*// ; # Chop datatype in parentheses off the front.
2024-05-05 02:03:44 -04:00
$str .= "\\subsection{Return Value}\n\n" ;
2024-06-12 15:49:59 -04:00
$str .= "$returns\n" ;
2024-05-05 02:03:44 -04: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-07 14:23:02 -04:00
s/\A\s*[\:\*\-]\s*// ;
2024-05-05 02:03:44 -04: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-01 19:58:21 -04:00
foreach ( @coverage_gap ) {
print ( "$_\n" );
}
2021-03-16 13:46:03 -04:00
}
# end of wikiheaders.pl ...