Simple Directmedia Layer
at main 149 kB view raw
1#!/usr/bin/perl -w 2 3# Simple DirectMedia Layer 4# Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 5# 6# This software is provided 'as-is', without any express or implied 7# warranty. In no event will the authors be held liable for any damages 8# arising from the use of this software. 9# 10# Permission is granted to anyone to use this software for any purpose, 11# including commercial applications, and to alter it and redistribute it 12# freely, subject to the following restrictions: 13# 14# 1. The origin of this software must not be misrepresented; you must not 15# claim that you wrote the original software. If you use this software 16# in a product, an acknowledgment in the product documentation would be 17# appreciated but is not required. 18# 2. Altered source versions must be plainly marked as such, and must not be 19# misrepresented as being the original software. 20# 3. This notice may not be removed or altered from any source distribution. 21 22use warnings; 23use strict; 24use File::Path; 25use Text::Wrap; 26 27$Text::Wrap::huge = 'overflow'; 28 29my $projectfullname = 'Simple Directmedia Layer'; 30my $projectshortname = 'SDL'; 31my $wikisubdir = ''; 32my $incsubdir = 'include'; 33my $readmesubdir = undef; 34my $apiprefixregex = undef; 35my $versionfname = 'include/SDL_version.h'; 36my $versionmajorregex = '\A\#define\s+SDL_MAJOR_VERSION\s+(\d+)\Z'; 37my $versionminorregex = '\A\#define\s+SDL_MINOR_VERSION\s+(\d+)\Z'; 38my $versionmicroregex = '\A\#define\s+SDL_MICRO_VERSION\s+(\d+)\Z'; 39my $mainincludefname = 'SDL.h'; 40my $selectheaderregex = '\ASDL.*?\.h\Z'; 41my $projecturl = 'https://libsdl.org/'; 42my $wikiurl = 'https://wiki.libsdl.org'; 43my $bugreporturl = 'https://github.com/libsdl-org/sdlwiki/issues/new'; 44my $srcpath = undef; 45my $wikipath = undef; 46my $wikireadmesubdir = 'README'; 47my $warn_about_missing = 0; 48my $copy_direction = 0; 49my $optionsfname = undef; 50my $wikipreamble = undef; 51my $wikiheaderfiletext = 'Defined in %fname%'; 52my $manpageheaderfiletext = 'Defined in %fname%'; 53my $headercategoryeval = undef; 54my $quickrefenabled = 0; 55my @quickrefcategoryorder; 56my $quickreftitle = undef; 57my $quickrefurl = undef; 58my $quickrefdesc = undef; 59my $quickrefmacroregex = undef; 60my $changeformat = undef; 61my $manpath = undef; 62my $gitrev = undef; 63 64foreach (@ARGV) { 65 $warn_about_missing = 1, next if $_ eq '--warn-about-missing'; 66 $copy_direction = 1, next if $_ eq '--copy-to-headers'; 67 $copy_direction = 1, next if $_ eq '--copy-to-header'; 68 $copy_direction = -1, next if $_ eq '--copy-to-wiki'; 69 $copy_direction = -2, next if $_ eq '--copy-to-manpages'; 70 $copy_direction = -3, next if $_ eq '--report-coverage-gaps'; 71 $copy_direction = -4, next if $_ eq '--copy-to-latex'; 72 if (/\A--options=(.*)\Z/) { 73 $optionsfname = $1; 74 next; 75 } elsif (/\A--changeformat=(.*)\Z/) { 76 $changeformat = $1; 77 next; 78 } elsif (/\A--manpath=(.*)\Z/) { 79 $manpath = $1; 80 next; 81 } elsif (/\A--rev=(.*)\Z/) { 82 $gitrev = $1; 83 next; 84 } 85 $srcpath = $_, next if not defined $srcpath; 86 $wikipath = $_, next if not defined $wikipath; 87} 88 89my $default_optionsfname = '.wikiheaders-options'; 90$default_optionsfname = "$srcpath/$default_optionsfname" if defined $srcpath; 91 92if ((not defined $optionsfname) && (-f $default_optionsfname)) { 93 $optionsfname = $default_optionsfname; 94} 95 96if (defined $optionsfname) { 97 open OPTIONS, '<', $optionsfname or die("Failed to open options file '$optionsfname': $!\n"); 98 while (<OPTIONS>) { 99 next if /\A\s*\#/; # Skip lines that start with (optional whitespace, then) '#' as comments. 100 101 chomp; 102 if (/\A(.*?)\=(.*)\Z/) { 103 my $key = $1; 104 my $val = $2; 105 $key =~ s/\A\s+//; 106 $key =~ s/\s+\Z//; 107 $val =~ s/\A\s+//; 108 $val =~ s/\s+\Z//; 109 $warn_about_missing = int($val), next if $key eq 'warn_about_missing'; 110 $srcpath = $val, next if $key eq 'srcpath'; 111 $wikipath = $val, next if $key eq 'wikipath'; 112 $apiprefixregex = $val, next if $key eq 'apiprefixregex'; 113 $projectfullname = $val, next if $key eq 'projectfullname'; 114 $projectshortname = $val, next if $key eq 'projectshortname'; 115 $wikisubdir = $val, next if $key eq 'wikisubdir'; 116 $incsubdir = $val, next if $key eq 'incsubdir'; 117 $readmesubdir = $val, next if $key eq 'readmesubdir'; 118 $versionmajorregex = $val, next if $key eq 'versionmajorregex'; 119 $versionminorregex = $val, next if $key eq 'versionminorregex'; 120 $versionmicroregex = $val, next if $key eq 'versionmicroregex'; 121 $versionfname = $val, next if $key eq 'versionfname'; 122 $mainincludefname = $val, next if $key eq 'mainincludefname'; 123 $selectheaderregex = $val, next if $key eq 'selectheaderregex'; 124 $projecturl = $val, next if $key eq 'projecturl'; 125 $wikiurl = $val, next if $key eq 'wikiurl'; 126 $bugreporturl = $val, next if $key eq 'bugreporturl'; 127 $wikipreamble = $val, next if $key eq 'wikipreamble'; 128 $wikiheaderfiletext = $val, next if $key eq 'wikiheaderfiletext'; 129 $manpageheaderfiletext = $val, next if $key eq 'manpageheaderfiletext'; 130 $headercategoryeval = $val, next if $key eq 'headercategoryeval'; 131 $quickrefenabled = int($val), next if $key eq 'quickrefenabled'; 132 @quickrefcategoryorder = split(/,/, $val), next if $key eq 'quickrefcategoryorder'; 133 $quickreftitle = $val, next if $key eq 'quickreftitle'; 134 $quickrefurl = $val, next if $key eq 'quickrefurl'; 135 $quickrefdesc = $val, next if $key eq 'quickrefdesc'; 136 $quickrefmacroregex = $val, next if $key eq 'quickrefmacroregex'; 137 } 138 } 139 close(OPTIONS); 140} 141 142sub escLaTeX { 143 my $str = shift; 144 $str =~ s/([_\#\&\^])/\\$1/g; 145 return $str; 146} 147 148my $wordwrap_mode = 'mediawiki'; 149sub wordwrap_atom { # don't call this directly. 150 my $str = shift; 151 my $retval = ''; 152 153 # wordwrap but leave links intact, even if they overflow. 154 if ($wordwrap_mode eq 'mediawiki') { 155 while ($str =~ s/(.*?)\s*(\[https?\:\/\/.*?\s+.*?\])\s*//ms) { 156 $retval .= fill('', '', $1); # wrap it. 157 $retval .= "\n$2\n"; # don't wrap it. 158 } 159 } elsif ($wordwrap_mode eq 'md') { 160 while ($str =~ s/(.*?)\s*(\[.*?\]\(https?\:\/\/.*?\))\s*//ms) { 161 $retval .= fill('', '', $1); # wrap it. 162 $retval .= "\n$2\n"; # don't wrap it. 163 } 164 } 165 166 return $retval . fill('', '', $str); 167} 168 169sub wordwrap_with_bullet_indent { # don't call this directly. 170 my $bullet = shift; 171 my $str = shift; 172 my $retval = ''; 173 174 #print("WORDWRAP BULLET ('$bullet'):\n\n$str\n\n"); 175 176 # You _can't_ (at least with Pandoc) have a bullet item with a newline in 177 # MediaWiki, so _remove_ wrapping! 178 if ($wordwrap_mode eq 'mediawiki') { 179 $retval = "$bullet$str"; 180 $retval =~ s/\n/ /gms; 181 $retval =~ s/\s+$//gms; 182 #print("WORDWRAP BULLET DONE:\n\n$retval\n\n"); 183 return "$retval\n"; 184 } 185 186 my $bulletlen = length($bullet); 187 188 # wrap it and then indent each line to be under the bullet. 189 $Text::Wrap::columns -= $bulletlen; 190 my @wrappedlines = split /\n/, wordwrap_atom($str); 191 $Text::Wrap::columns += $bulletlen; 192 193 my $prefix = $bullet; 194 my $usual_prefix = ' ' x $bulletlen; 195 196 foreach (@wrappedlines) { 197 s/\s*\Z//; 198 $retval .= "$prefix$_\n"; 199 $prefix = $usual_prefix; 200 } 201 202 return $retval; 203} 204 205sub wordwrap_one_paragraph { # don't call this directly. 206 my $retval = ''; 207 my $p = shift; 208 #print "\n\n\nPARAGRAPH: [$p]\n\n\n"; 209 if ($p =~ s/\A([\*\-] )//) { # bullet list, starts with "* " or "- ". 210 my $bullet = $1; 211 my $item = ''; 212 my @items = split /\n/, $p; 213 foreach (@items) { 214 if (s/\A([\*\-] )//) { 215 $retval .= wordwrap_with_bullet_indent($bullet, $item); 216 $item = ''; 217 } 218 s/\A\s*//; 219 $item .= "$_\n"; # accumulate lines until we hit the end or another bullet. 220 } 221 if ($item ne '') { 222 $retval .= wordwrap_with_bullet_indent($bullet, $item); 223 } 224 } elsif ($p =~ /\A\s*\|.*\|\s*\n/) { # Markdown table 225 $retval = "$p\n"; # don't wrap it (!!! FIXME: but maybe parse by lines until we run out of table...) 226 } else { 227 $retval = wordwrap_atom($p) . "\n"; 228 } 229 230 return $retval; 231} 232 233sub wordwrap_paragraphs { # don't call this directly. 234 my $str = shift; 235 my $retval = ''; 236 my @paragraphs = split /\n\n/, $str; 237 foreach (@paragraphs) { 238 next if $_ eq ''; 239 $retval .= wordwrap_one_paragraph($_); 240 $retval .= "\n"; 241 } 242 return $retval; 243} 244 245my $wordwrap_default_columns = 76; 246sub wordwrap { 247 my $str = shift; 248 my $columns = shift; 249 250 $columns = $wordwrap_default_columns if not defined $columns; 251 $columns += $wordwrap_default_columns if $columns < 0; 252 $Text::Wrap::columns = $columns; 253 254 my $retval = ''; 255 256 #print("\n\nWORDWRAP:\n\n$str\n\n\n"); 257 258 $str =~ s/\A\n+//ms; 259 260 while ($str =~ s/(.*?)(\`\`\`.*?\`\`\`|\<syntaxhighlight.*?\<\/syntaxhighlight\>)//ms) { 261 #print("\n\nWORDWRAP BLOCK:\n\n$1\n\n ===\n\n$2\n\n\n"); 262 $retval .= wordwrap_paragraphs($1); # wrap it. 263 $retval .= "$2\n\n"; # don't wrap it. 264 } 265 266 $retval .= wordwrap_paragraphs($str); # wrap what's left. 267 $retval =~ s/\n+\Z//ms; 268 269 #print("\n\nWORDWRAP DONE:\n\n$retval\n\n\n"); 270 return $retval; 271} 272 273# This assumes you're moving from Markdown (in the Doxygen data) to Wiki, which 274# is why the 'md' section is so sparse. 275sub wikify_chunk { 276 my $wikitype = shift; 277 my $str = shift; 278 my $codelang = shift; 279 my $code = shift; 280 281 #print("\n\nWIKIFY CHUNK:\n\n$str\n\n\n"); 282 283 if ($wikitype eq 'mediawiki') { 284 # convert `code` things first, so they aren't mistaken for other markdown items. 285 my $codedstr = ''; 286 while ($str =~ s/\A(.*?)\`(.*?)\`//ms) { 287 my $codeblock = $2; 288 $codedstr .= wikify_chunk($wikitype, $1, undef, undef); 289 if (defined $apiprefixregex) { 290 # Convert obvious API things to wikilinks, even inside `code` blocks. 291 $codeblock =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[\[$2\]\]/gms; 292 } 293 $codedstr .= "<code>$codeblock</code>"; 294 } 295 296 # Convert obvious API things to wikilinks. 297 if (defined $apiprefixregex) { 298 $str =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[\[$2\]\]/gms; 299 } 300 301 # Make some Markdown things into MediaWiki... 302 303 # links 304 $str =~ s/\[(.*?)\]\((https?\:\/\/.*?)\)/\[$2 $1\]/g; 305 306 # bold+italic 307 $str =~ s/\*\*\*(.*?)\*\*\*/'''''$1'''''/gms; 308 309 # bold 310 $str =~ s/\*\*(.*?)\*\*/'''$1'''/gms; 311 312 # italic 313 $str =~ s/\*(.*?)\*/''$1''/gms; 314 315 # bullets 316 $str =~ s/^\- /* /gm; 317 318 $str = $codedstr . $str; 319 320 if (defined $code) { 321 $str .= "<syntaxhighlight lang='$codelang'>$code<\/syntaxhighlight>"; 322 } 323 } elsif ($wikitype eq 'md') { 324 # convert `code` things first, so they aren't mistaken for other markdown items. 325 my $codedstr = ''; 326 while ($str =~ s/\A(.*?)(\`.*?\`)//ms) { 327 my $codeblock = $2; 328 $codedstr .= wikify_chunk($wikitype, $1, undef, undef); 329 if (defined $apiprefixregex) { 330 # Convert obvious API things to wikilinks, even inside `code` blocks, 331 # BUT ONLY IF the entire code block is the API thing, 332 # So something like "just call `SDL_Whatever`" will become 333 # "just call [`SDL_Whatever`](SDL_Whatever)", but 334 # "just call `SDL_Whatever(7)`" will not. It's just the safest 335 # way to do this without resorting to wrapping things in html <code> tags. 336 $codeblock =~ s/\A\`($apiprefixregex[a-zA-Z0-9_]+)\`\Z/[`$1`]($1)/gms; 337 } 338 $codedstr .= $codeblock; 339 } 340 341 # Convert obvious API things to wikilinks. 342 if (defined $apiprefixregex) { 343 $str =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[$2\]\($2\)/gms; 344 } 345 346 $str = $codedstr . $str; 347 348 if (defined $code) { 349 $str .= "```$codelang\n$code\n```\n"; 350 } 351 } 352 353 #print("\n\nWIKIFY CHUNK DONE:\n\n$str\n\n\n"); 354 355 return $str; 356} 357 358sub wikify { 359 my $wikitype = shift; 360 my $str = shift; 361 my $retval = ''; 362 363 #print("WIKIFY WHOLE:\n\n$str\n\n\n"); 364 365 while ($str =~ s/\A(.*?)\`\`\`(.*?)\n(.*?)\n\`\`\`(\n|\Z)//ms) { 366 $retval .= wikify_chunk($wikitype, $1, $2, $3); 367 } 368 $retval .= wikify_chunk($wikitype, $str, undef, undef); 369 370 #print("WIKIFY WHOLE DONE:\n\n$retval\n\n\n"); 371 372 return $retval; 373} 374 375 376my $dewikify_mode = 'md'; 377my $dewikify_manpage_code_indent = 1; 378 379sub dewikify_chunk { 380 my $wikitype = shift; 381 my $str = shift; 382 my $codelang = shift; 383 my $code = shift; 384 385 #print("\n\nDEWIKIFY CHUNK:\n\n$str\n\n\n"); 386 387 if ($dewikify_mode eq 'md') { 388 if ($wikitype eq 'mediawiki') { 389 # Doxygen supports Markdown (and it just simply looks better than MediaWiki 390 # when looking at the raw headers), so do some conversions here as necessary. 391 392 # Dump obvious wikilinks. 393 if (defined $apiprefixregex) { 394 $str =~ s/\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]/$1/gms; 395 } 396 397 # links 398 $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\[$2\]\($1\)/g; 399 400 # <code></code> is also popular. :/ 401 $str =~ s/\<code>(.*?)<\/code>/`$1`/gms; 402 403 # bold+italic 404 $str =~ s/'''''(.*?)'''''/***$1***/gms; 405 406 # bold 407 $str =~ s/'''(.*?)'''/**$1**/gms; 408 409 # italic 410 $str =~ s/''(.*?)''/*$1*/gms; 411 412 # bullets 413 $str =~ s/^\* /- /gm; 414 } elsif ($wikitype eq 'md') { 415 # Dump obvious wikilinks. The rest can just passthrough. 416 if (defined $apiprefixregex) { 417 $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/$1/gms; 418 } 419 } 420 421 if (defined $code) { 422 $str .= "\n```$codelang\n$code\n```\n"; 423 } 424 } elsif ($dewikify_mode eq 'manpage') { 425 $str =~ s/\./\\[char46]/gms; # make sure these can't become control codes. 426 if ($wikitype eq 'mediawiki') { 427 # Dump obvious wikilinks. 428 if (defined $apiprefixregex) { 429 $str =~ s/\s*\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]\s*/\n.BR $1\n/gms; 430 } 431 432 # links 433 $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\n.URL "$1" "$2"\n/g; 434 435 # <code></code> is also popular. :/ 436 $str =~ s/\s*\<code>(.*?)<\/code>\s*/\n.BR $1\n/gms; 437 438 # bold+italic (this looks bad, just make it bold). 439 $str =~ s/\s*'''''(.*?)'''''\s*/\n.B $1\n/gms; 440 441 # bold 442 $str =~ s/\s*'''(.*?)'''\s*/\n.B $1\n/gms; 443 444 # italic 445 $str =~ s/\s*''(.*?)''\s*/\n.I $1\n/gms; 446 447 # bullets 448 $str =~ s/^\* /\n\\\(bu /gm; 449 } elsif ($wikitype eq 'md') { 450 # Dump obvious wikilinks. 451 if (defined $apiprefixregex) { 452 $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/\n.BR $1\n/gms; 453 } 454 455 # links 456 $str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\n.URL "$2" "$1"\n/g; 457 458 # <code></code> is also popular. :/ 459 $str =~ s/\s*\`(.*?)\`\s*/\n.BR $1\n/gms; 460 461 # bold+italic (this looks bad, just make it bold). 462 $str =~ s/\s*\*\*\*(.*?)\*\*\*\s*/\n.B $1\n/gms; 463 464 # bold 465 $str =~ s/\s*\*\*(.*?)\*\*\s*/\n.B $1\n/gms; 466 467 # italic 468 $str =~ s/\s*\*(.*?)\*\s*/\n.I $1\n/gms; 469 470 # bullets 471 $str =~ s/^\- /\n\\\(bu /gm; 472 } 473 474 if (defined $code) { 475 $code =~ s/\A\n+//gms; 476 $code =~ s/\n+\Z//gms; 477 if ($dewikify_manpage_code_indent) { 478 $str .= "\n.IP\n" 479 } else { 480 $str .= "\n.PP\n" 481 } 482 $str .= ".EX\n$code\n.EE\n.PP\n"; 483 } 484 } elsif ($dewikify_mode eq 'LaTeX') { 485 if ($wikitype eq 'mediawiki') { 486 # Dump obvious wikilinks. 487 if (defined $apiprefixregex) { 488 $str =~ s/\s*\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]/$1/gms; 489 } 490 491 # links 492 $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\\href{$1}{$2}/g; 493 494 # <code></code> is also popular. :/ 495 $str =~ s/\s*\<code>(.*?)<\/code>/ \\texttt{$1}/gms; 496 497 # bold+italic 498 $str =~ s/\s*'''''(.*?)'''''/ \\textbf{\\textit{$1}}/gms; 499 500 # bold 501 $str =~ s/\s*'''(.*?)'''/ \\textbf{$1}/gms; 502 503 # italic 504 $str =~ s/\s*''(.*?)''/ \\textit{$1}/gms; 505 506 # bullets 507 $str =~ s/^\*\s+/ \\item /gm; 508 } elsif ($wikitype eq 'md') { 509 # Dump obvious wikilinks. 510 if (defined $apiprefixregex) { 511 $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/$1/gms; 512 } 513 514 # links 515 $str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\\href{$2}{$1}/g; 516 517 # <code></code> is also popular. :/ 518 $str =~ s/\s*\`(.*?)\`/ \\texttt{$1}/gms; 519 520 # bold+italic 521 $str =~ s/\s*\*\*\*(.*?)\*\*\*/ \\textbf{\\textit{$1}}/gms; 522 523 # bold 524 $str =~ s/\s*\*\*(.*?)\*\*/ \\textbf{$1}/gms; 525 526 # italic 527 $str =~ s/\s*\*(.*?)\*/ \\textit{$1}/gms; 528 529 # bullets 530 $str =~ s/^\-\s+/ \\item /gm; 531 } 532 533 # Wrap bullet lists in itemize blocks... 534 $str =~ s/^(\s*\\item .*?)(\n\n|\Z)/\n\\begin{itemize}\n$1$2\n\\end{itemize}\n\n/gms; 535 536 $str = escLaTeX($str); 537 538 if (defined $code) { 539 $code =~ s/\A\n+//gms; 540 $code =~ s/\n+\Z//gms; 541 542 if (($codelang eq '') || ($codelang eq 'output')) { 543 $str .= "\\begin{verbatim}\n$code\n\\end{verbatim}\n"; 544 } else { 545 if ($codelang eq 'c') { 546 $codelang = 'C'; 547 } elsif ($codelang eq 'c++') { 548 $codelang = 'C++'; 549 } else { 550 die("Unexpected codelang '$codelang'"); 551 } 552 $str .= "\n\\lstset{language=$codelang}\n"; 553 $str .= "\\begin{lstlisting}\n$code\n\\end{lstlisting}\n"; 554 } 555 } 556 } else { 557 die("Unexpected dewikify_mode"); 558 } 559 560 #print("\n\nDEWIKIFY CHUNK DONE:\n\n$str\n\n\n"); 561 562 return $str; 563} 564 565sub dewikify { 566 my $wikitype = shift; 567 my $str = shift; 568 return '' if not defined $str; 569 570 #print("DEWIKIFY WHOLE:\n\n$str\n\n\n"); 571 572 $str =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms; 573 $str =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms; 574 575 my $retval = ''; 576 if ($wikitype eq 'mediawiki') { 577 while ($str =~ s/\A(.*?)<syntaxhighlight lang='?(.*?)'?>(.*?)<\/syntaxhighlight\>//ms) { 578 $retval .= dewikify_chunk($wikitype, $1, $2, $3); 579 } 580 } elsif ($wikitype eq 'md') { 581 while ($str =~ s/\A(.*?)\n```(.*?)\n(.*?)\n```\n//ms) { 582 $retval .= dewikify_chunk($wikitype, $1, $2, $3); 583 } 584 } 585 $retval .= dewikify_chunk($wikitype, $str, undef, undef); 586 587 #print("DEWIKIFY WHOLE DONE:\n\n$retval\n\n\n"); 588 589 return $retval; 590} 591 592sub filecopy { 593 my $src = shift; 594 my $dst = shift; 595 my $endline = shift; 596 $endline = "\n" if not defined $endline; 597 598 open(COPYIN, '<', $src) or die("Failed to open '$src' for reading: $!\n"); 599 open(COPYOUT, '>', $dst) or die("Failed to open '$dst' for writing: $!\n"); 600 while (<COPYIN>) { 601 chomp; 602 s/[ \t\r\n]*\Z//; 603 print COPYOUT "$_$endline"; 604 } 605 close(COPYOUT); 606 close(COPYIN); 607} 608 609sub usage { 610 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"); 611} 612 613usage() if not defined $srcpath; 614usage() if not defined $wikipath; 615#usage() if $copy_direction == 0; 616 617if (not defined $manpath) { 618 $manpath = "$srcpath/man"; 619} 620 621my @standard_wiki_sections = ( 622 'Draft', 623 '[Brief]', 624 'Deprecated', 625 'Header File', 626 'Syntax', 627 'Function Parameters', 628 'Macro Parameters', 629 'Fields', 630 'Values', 631 'Return Value', 632 'Remarks', 633 'Thread Safety', 634 'Version', 635 'Code Examples', 636 'See Also' 637); 638 639# Sections that only ever exist in the wiki and shouldn't be deleted when 640# not found in the headers. 641my %only_wiki_sections = ( # The ones don't mean anything, I just need to check for key existence. 642 'Draft', 1, 643 'Code Examples', 1, 644 'Header File', 1 645); 646 647 648my %headers = (); # $headers{"SDL_audio.h"} -> reference to an array of all lines of text in SDL_audio.h. 649my %headersyms = (); # $headersyms{"SDL_OpenAudio"} -> string of header documentation for SDL_OpenAudio, with comment '*' bits stripped from the start. Newlines embedded! 650my %headerdecls = (); 651my %headersymslocation = (); # $headersymslocation{"SDL_OpenAudio"} -> name of header holding SDL_OpenAudio define ("SDL_audio.h" in this case). 652my %headersymschunk = (); # $headersymschunk{"SDL_OpenAudio"} -> offset in array in %headers that should be replaced for this symbol. 653my %headersymshasdoxygen = (); # $headersymshasdoxygen{"SDL_OpenAudio"} -> 1 if there was no existing doxygen for this function. 654my %headersymstype = (); # $headersymstype{"SDL_OpenAudio"} -> 1 (function), 2 (macro), 3 (struct), 4 (enum), 5 (other typedef) 655my %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. 656my %headercategorydocs = (); # $headercategorydocs{"Audio"} -> (fake) symbol for this category's documentation. Undefined if not documented. 657my %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. 658my %headersymsrettype = (); # $headersymsrettype{"SDL_OpenAudio"} -> string of C datatype of return value. Undef'd if not a function. 659my %wikitypes = (); # contains string of wiki page extension, like $wikitypes{"SDL_OpenAudio"} == 'mediawiki' 660my %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"}. 661my %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' 662my %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' 663 664my %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. 665 666my @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! 667 668sub add_coverage_gap { 669 if ($copy_direction == -3) { # --report-coverage-gaps 670 my $text = shift; 671 my $dent = shift; 672 my $lineno = shift; 673 return if $text =~ /\A\s*\Z/; # skip blank lines 674 return if $text =~ /\A\s*\#\s*(if|el|endif|include)/; # skip preprocessor floof. 675 push @coverage_gap, "$dent:$lineno: $text"; 676 } 677} 678 679sub print_undocumented_section { 680 my $fh = shift; 681 my $typestr = shift; 682 my $typeval = shift; 683 684 print $fh "## $typestr defined in the headers, but not in the wiki\n\n"; 685 my $header_only_sym = 0; 686 foreach (sort keys %headersyms) { 687 my $sym = $_; 688 if ((not defined $wikisyms{$sym}) && ($headersymstype{$sym} == $typeval)) { 689 print $fh "- [$sym]($sym)\n"; 690 $header_only_sym = 1; 691 } 692 } 693 if (!$header_only_sym) { 694 print $fh "(none)\n"; 695 } 696 print $fh "\n"; 697 698 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?) 699 print $fh "## $typestr defined in the wiki, but not in the headers\n\n"; 700 701 my $wiki_only_sym = 0; 702 foreach (sort keys %wikisyms) { 703 my $sym = $_; 704 if ((not defined $headersyms{$sym}) && ($headersymstype{$sym} == $typeval)) { 705 print $fh "- [$sym]($sym)\n"; 706 $wiki_only_sym = 1; 707 } 708 } 709 if (!$wiki_only_sym) { 710 print $fh "(none)\n"; 711 } 712 print $fh "\n"; 713 } 714} 715 716sub strip_fn_declaration_metadata { 717 my $decl = shift; 718 $decl =~ s/SDL_(PRINTF|SCANF)_FORMAT_STRING\s*//; # don't want this metadata as part of the documentation. 719 $decl =~ s/SDL_ALLOC_SIZE2?\(.*?\)\s*//; # don't want this metadata as part of the documentation. 720 $decl =~ s/SDL_MALLOC\s*//; # don't want this metadata as part of the documentation. 721 $decl =~ s/SDL_(IN|OUT|INOUT)_.*?CAP\s*\(.*?\)\s*//g; # don't want this metadata as part of the documentation. 722 $decl =~ s/\)(\s*SDL_[a-zA-Z_]+(\(.*?\)|))*;/);/; # don't want this metadata as part of the documentation. 723 return $decl; 724} 725 726sub sanitize_c_typename { 727 my $str = shift; 728 $str =~ s/\A\s+//; 729 $str =~ s/\s+\Z//; 730 $str =~ s/const\s*(\*+)/const $1/g; # one space between `const` and pointer stars: `char const* const *` becomes `char const * const *`. 731 $str =~ s/\*\s+\*/**/g; # drop spaces between pointers: `void * *` becomes `void **`. 732 $str =~ s/\s*(\*+)\Z/ $1/; # one space between pointer stars and what it points to: `void**` becomes `void **`. 733 return $str; 734} 735 736my %big_ascii = ( 737 '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}" ], 738 '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}" ], 739 '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}" ], 740 '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}" ], 741 '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}" ], 742 '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}" ], 743 '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}" ], 744 '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}" ], 745 '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}" ], 746 '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}" ], 747 '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}" ], 748 '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}" ], 749 '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}" ], 750 '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}" ], 751 '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}" ], 752 '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}" ], 753 '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}" ], 754 '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}" ], 755 '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}" ], 756 '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}" ], 757 '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}" ], 758 '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}" ], 759 '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}" ], 760 '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}" ], 761 '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}" ], 762 '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}" ], 763 ' ' => [ "\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}" ], 764 '.' => [ "\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}" ], 765 ',' => [ "\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}" ], 766 '/' => [ "\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}" ], 767 '!' => [ "\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}" ], 768 '_' => [ "\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}" ], 769 '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}" ], 770 '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}" ], 771 '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}" ], 772 '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}" ], 773 '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}" ], 774 '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}" ], 775 '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}" ], 776 '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}" ], 777 '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}" ], 778 '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}" ], 779); 780 781sub print_big_ascii_string { 782 my $fh = shift; 783 my $str = shift; 784 my $comment = shift; 785 my $lowascii = shift; 786 $comment = '' if not defined $comment; 787 $lowascii = 0 if not defined $lowascii; 788 789 my @chars = split //, $str; 790 my $charcount = scalar(@chars); 791 792 binmode($fh, ":utf8"); 793 794 my $maxrows = $lowascii ? 5 : 6; 795 796 for(my $rownum = 0; $rownum < $maxrows; $rownum++){ 797 print $fh $comment; 798 my $charidx = 0; 799 foreach my $ch (@chars) { 800 my $rowsref = $big_ascii{uc($ch)}; 801 die("Don't have a big ascii entry for '$ch'!\n") if not defined $rowsref; 802 my $row = @$rowsref[$rownum]; 803 804 if ($lowascii) { 805 my @x = split //, $row; 806 foreach (@x) { 807 my $v = ($_ eq "\x{2588}") ? 'X' : ' '; 808 print $fh $v; 809 } 810 } else { 811 print $fh $row; 812 } 813 814 $charidx++; 815 816 if ($charidx < $charcount) { 817 print $fh " "; 818 } 819 } 820 print $fh "\n"; 821 } 822} 823 824sub generate_quickref { 825 my $briefsref = shift; 826 my $path = shift; 827 my $lowascii = shift; 828 829 # !!! FIXME: this gitrev and majorver/etc stuff is copy/pasted a few times now. 830 if (!$gitrev) { 831 $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`; 832 chomp($gitrev); 833 } 834 835 # !!! FIXME 836 open(FH, '<', "$srcpath/$versionfname") or die("Can't open '$srcpath/$versionfname': $!\n"); 837 my $majorver = 0; 838 my $minorver = 0; 839 my $microver = 0; 840 while (<FH>) { 841 chomp; 842 if (/$versionmajorregex/) { 843 $majorver = int($1); 844 } elsif (/$versionminorregex/) { 845 $minorver = int($1); 846 } elsif (/$versionmicroregex/) { 847 $microver = int($1); 848 } 849 } 850 close(FH); 851 my $fullversion = "$majorver.$minorver.$microver"; 852 853 my $tmppath = "$path.tmp"; 854 open(my $fh, '>', $tmppath) or die("Can't open '$tmppath': $!\n"); 855 856 if (not @quickrefcategoryorder) { 857 @quickrefcategoryorder = sort keys %headercategorydocs; 858 } 859 860 #my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time); 861 #my $datestr = sprintf("%04d-%02d-%02d %02d:%02d:%02d GMT", $year+1900, $mon+1, $mday, $hour, $min, $sec); 862 863 print $fh "<!-- DO NOT EDIT THIS PAGE ON THE WIKI. IT WILL BE OVERWRITTEN BY WIKIHEADERS AND CHANGES WILL BE LOST! -->\n\n"; 864 865 # Just something to test big_ascii output. 866 #print_big_ascii_string($fh, "ABCDEFGHIJ", '', $lowascii); 867 #print_big_ascii_string($fh, "KLMNOPQRST", '', $lowascii); 868 #print_big_ascii_string($fh, "UVWXYZ0123", '', $lowascii); 869 #print_big_ascii_string($fh, "456789JT3A", '', $lowascii); 870 #print_big_ascii_string($fh, "hello, _a.b/c_!!", '', $lowascii); 871 872 # Dan Bechard's work was on an SDL2 cheatsheet: 873 # https://blog.theprogrammingjunkie.com/post/sdl2-cheatsheet/ 874 875 if ($lowascii) { 876 print $fh "# QuickReferenceNoUnicode\n\n"; 877 print $fh "If you want to paste this into a text editor that can handle\n"; 878 print $fh "fancy Unicode section headers, try using\n"; 879 print $fh "[QuickReference](QuickReference) instead.\n\n"; 880 } else { 881 print $fh "# QuickReference\n\n"; 882 print $fh "If you want to paste this into a text editor that can't handle\n"; 883 print $fh "the fancy Unicode section headers, try using\n"; 884 print $fh "[QuickReferenceNoUnicode](QuickReferenceNoUnicode) instead.\n\n"; 885 } 886 887 print $fh "```c\n"; 888 print $fh "// $quickreftitle\n" if defined $quickreftitle; 889 print $fh "//\n"; 890 print $fh "// $quickrefurl\n//\n" if defined $quickrefurl; 891 print $fh "// $quickrefdesc\n" if defined $quickrefdesc; 892 #print $fh "// When this document was written: $datestr\n"; 893 print $fh "// Based on $projectshortname version $fullversion\n"; 894 #print $fh "// git revision $gitrev\n"; 895 print $fh "//\n"; 896 print $fh "// This can be useful in an IDE with search and syntax highlighting.\n"; 897 print $fh "//\n"; 898 print $fh "// Original idea for this document came from Dan Bechard (thanks!)\n"; 899 print $fh "// ASCII art generated by: https://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow (with modified 'S' for readability)\n\n"; 900 901 foreach (@quickrefcategoryorder) { 902 my $cat = $_; 903 my $maxlen = 0; 904 my @csigs = (); 905 my $funcorderref = $quickreffuncorder{$cat}; 906 next if not defined $funcorderref; 907 908 foreach (@$funcorderref) { 909 my $sym = $_; 910 my $csig = ''; 911 912 if ($headersymstype{$sym} == 1) { # function 913 $csig = "${headersymsrettype{$sym}} $sym"; 914 my $fnsigparams = $headersymsparaminfo{$sym}; 915 if (not defined($fnsigparams)) { 916 $csig .= '(void);'; 917 } else { 918 my $sep = '('; 919 for (my $i = 0; $i < scalar(@$fnsigparams); $i += 2) { 920 my $paramname = @$fnsigparams[$i]; 921 my $paramtype = @$fnsigparams[$i+1]; 922 my $spc = ($paramtype =~ /\*\Z/) ? '' : ' '; 923 $csig .= "$sep$paramtype$spc$paramname"; 924 $sep = ', '; 925 } 926 $csig .= ");"; 927 } 928 } elsif ($headersymstype{$sym} == 2) { # macro 929 next if defined $quickrefmacroregex && not $sym =~ /$quickrefmacroregex/; 930 931 $csig = (split /\n/, $headerdecls{$sym})[0]; # get the first line from a multiline string. 932 if (not $csig =~ s/\A(\#define [a-zA-Z0-9_]*\(.*?\))(\s+.*)?\Z/$1/) { 933 $csig =~ s/\A(\#define [a-zA-Z0-9_]*)(\s+.*)?\Z/$1/; 934 } 935 chomp($csig); 936 } 937 938 my $len = length($csig); 939 $maxlen = $len if $len > $maxlen; 940 941 push @csigs, $sym; 942 push @csigs, $csig; 943 } 944 945 $maxlen += 2; 946 947 next if (not @csigs); 948 949 print $fh "\n"; 950 print_big_ascii_string($fh, $cat, '// ', $lowascii); 951 print $fh "\n"; 952 953 while (@csigs) { 954 my $sym = shift @csigs; 955 my $csig = shift @csigs; 956 my $brief = $$briefsref{$sym}; 957 if (defined $brief) { 958 $brief = "$brief"; 959 chomp($brief); 960 my $thiswikitype = defined $wikitypes{$sym} ? $wikitypes{$sym} : 'md'; # default to MarkDown for new stuff. 961 $brief = dewikify($thiswikitype, $brief); 962 my $spaces = ' ' x ($maxlen - length($csig)); 963 $brief = "$spaces// $brief"; 964 } else { 965 $brief = ''; 966 } 967 print $fh "$csig$brief\n"; 968 } 969 } 970 971 print $fh "```\n\n"; 972 973 close($fh); 974 975# # Don't overwrite the file if nothing has changed besides the timestamp 976# # and git revision. 977# my $matches = 1; 978# if ( not -f $path ) { 979# $matches = 0; # always write if the file hasn't been created yet. 980# } else { 981# open(my $fh_a, '<', $tmppath) or die("Can't open '$tmppath': $!\n"); 982# open(my $fh_b, '<', $path) or die("Can't open '$path': $!\n"); 983# while (1) { 984# my $a = <$fh_a>; 985# my $b = <$fh_b>; 986# $matches = 0, last if ((not defined $a) != (not defined $b)); 987# last if ((not defined $a) || (not defined $b)); 988# if ($a ne $b) { 989# next if ($a =~ /\A\/\/ When this document was written:/); 990# next if ($a =~ /\A\/\/ git revision /); 991# $matches = 0; 992# last; 993# } 994# } 995# 996# close($fh_a); 997# close($fh_b); 998# } 999# 1000# if ($matches) { 1001# unlink($tmppath); # it's the same file except maybe the date/gitrev. Don't overwrite it. 1002# } else { 1003# rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n"); 1004# } 1005 rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n"); 1006} 1007 1008 1009my $incpath = "$srcpath"; 1010$incpath .= "/$incsubdir" if $incsubdir ne ''; 1011 1012my $wikireadmepath = "$wikipath/$wikireadmesubdir"; 1013my $readmepath = undef; 1014if (defined $readmesubdir) { 1015 $readmepath = "$srcpath/$readmesubdir"; 1016} 1017 1018opendir(DH, $incpath) or die("Can't opendir '$incpath': $!\n"); 1019while (my $d = readdir(DH)) { 1020 my $dent = $d; 1021 next if not $dent =~ /$selectheaderregex/; # just selected headers. 1022 open(FH, '<', "$incpath/$dent") or die("Can't open '$incpath/$dent': $!\n"); 1023 1024 # You can optionally set a wiki category with Perl code in .wikiheaders-options that gets eval()'d per-header, 1025 # and also if you put `/* WIKI CATEGORY: blah */` on a line by itself, it'll change the category for any symbols 1026 # below it in the same file. If no category is set, one won't be added for the symbol (beyond the standard CategoryFunction, etc) 1027 my $current_wiki_category = undef; 1028 if (defined $headercategoryeval) { 1029 $_ = $dent; 1030 $current_wiki_category = eval($headercategoryeval); 1031 if (($current_wiki_category eq '') || ($current_wiki_category eq '-')) { 1032 $current_wiki_category = undef; 1033 } 1034 #print("CATEGORY FOR '$dent' IS " . (defined($current_wiki_category) ? "'$current_wiki_category'" : '(undef)') . "\n"); 1035 } 1036 1037 my @contents = (); 1038 my @function_order = (); 1039 my $ignoring_lines = 0; 1040 my $header_comment = -1; 1041 my $saw_category_doxygen = -1; 1042 my $lineno = 0; 1043 1044 while (<FH>) { 1045 chomp; 1046 $lineno++; 1047 my $symtype = 0; # nothing, yet. 1048 my $decl; 1049 my @templines; 1050 my $str; 1051 my $has_doxygen = 1; 1052 1053 # 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. 1054 # Functions and other things are a different story, though! 1055 1056 if ($header_comment == -1) { 1057 $header_comment = /\A\/\*\s*\Z/ ? 1 : 0; 1058 } elsif (($header_comment == 1) && (/\A\*\/\s*\Z/)) { 1059 $header_comment = 0; 1060 } 1061 1062 if ($ignoring_lines && /\A\s*\#\s*endif\s*\Z/) { 1063 $ignoring_lines = 0; 1064 push @contents, $_; 1065 next; 1066 } elsif ($ignoring_lines) { 1067 push @contents, $_; 1068 next; 1069 } elsif (/\A\s*\#\s*ifndef\s+SDL_WIKI_DOCUMENTATION_SECTION\s*\Z/) { 1070 $ignoring_lines = 1; 1071 push @contents, $_; 1072 next; 1073 } elsif (/\A\s*\/\*\s*WIKI CATEGORY:\s*(.*?)\s*\*\/\s*\Z/) { 1074 $current_wiki_category = (($1 eq '') || ($1 eq '-')) ? undef : $1; 1075 #print("CATEGORY FOR '$dent' CHANGED TO " . (defined($current_wiki_category) ? "'$current_wiki_category'" : '(undef)') . "\n"); 1076 push @contents, $_; 1077 next; 1078 } elsif (/\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/) { # a function declaration without a doxygen comment? 1079 $symtype = 1; # function declaration 1080 @templines = (); 1081 $decl = $_; 1082 $str = ''; 1083 $has_doxygen = 0; 1084 } elsif (/\A\s*SDL_FORCE_INLINE/) { # a (forced-inline) function declaration without a doxygen comment? 1085 $symtype = 1; # function declaration 1086 @templines = (); 1087 $decl = $_; 1088 $str = ''; 1089 $has_doxygen = 0; 1090 } elsif (not /\A\/\*\*\s*\Z/) { # not doxygen comment start? 1091 push @contents, $_; 1092 add_coverage_gap($_, $dent, $lineno) if ($header_comment == 0); 1093 next; 1094 } else { # Start of a doxygen comment, parse it out. 1095 my $is_category_doxygen = 0; 1096 1097 @templines = ( $_ ); 1098 while (<FH>) { 1099 chomp; 1100 $lineno++; 1101 push @templines, $_; 1102 last if /\A\s*\*\/\Z/; 1103 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... 1104 $str .= "$_\n"; 1105 while (<FH>) { 1106 chomp; 1107 $lineno++; 1108 push @templines, $_; 1109 s/\A\s*\*\s?//; 1110 if (s/\A\s*\`\`\`/```/) { 1111 $str .= "$_\n"; 1112 last; 1113 } else { 1114 $str .= "$_\n"; 1115 } 1116 } 1117 } else { 1118 s/\A\s*\*\s*//; # Strip off the " * " at the start of the comment line. 1119 1120 # To add documentation to Category Pages, the rule is it has to 1121 # be the first Doxygen comment in the header, and it must start with `# CategoryX` 1122 # (otherwise we'll treat it as documentation for whatever's below it). `X` is 1123 # the category name, which doesn't _necessarily_ have to match 1124 # $current_wiki_category, but it probably should. 1125 # 1126 # For compatibility with Doxygen, if there's a `\file` here instead of 1127 # `# CategoryName`, we'll accept it and use the $current_wiki_category if set. 1128 if ($saw_category_doxygen == -1) { 1129 $saw_category_doxygen = defined($current_wiki_category) && /\A\\file\s+/; 1130 if ($saw_category_doxygen) { 1131 $_ = "# Category$current_wiki_category"; 1132 } else { 1133 $saw_category_doxygen = /\A\# Category/; 1134 } 1135 $is_category_doxygen = $saw_category_doxygen; 1136 } 1137 1138 $str .= "$_\n"; 1139 } 1140 } 1141 1142 if ($is_category_doxygen) { 1143 $str =~ s/\s*\Z//; 1144 $decl = ''; 1145 $symtype = -1; # not a symbol at all. 1146 } else { 1147 $decl = <FH>; 1148 $lineno++ if defined $decl; 1149 $decl = '' if not defined $decl; 1150 chomp($decl); 1151 if ($decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/) { 1152 $symtype = 1; # function declaration 1153 } elsif ($decl =~ /\A\s*SDL_FORCE_INLINE/) { 1154 $symtype = 1; # (forced-inline) function declaration 1155 } elsif ($decl =~ /\A\s*\#\s*define\s+/) { 1156 $symtype = 2; # macro 1157 } elsif ($decl =~ /\A\s*(typedef\s+|)(struct|union)\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\Z)/) { 1158 $symtype = 3; # struct or union 1159 } elsif ($decl =~ /\A\s*(typedef\s+|)enum\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\Z)/) { 1160 $symtype = 4; # enum 1161 } elsif ($decl =~ /\A\s*typedef\s+.*\Z/) { 1162 $symtype = 5; # other typedef 1163 } else { 1164 #print "Found doxygen but no function sig:\n$str\n\n"; 1165 foreach (@templines) { 1166 push @contents, $_; 1167 add_coverage_gap($_, $dent, $lineno); 1168 } 1169 push @contents, $decl; 1170 add_coverage_gap($decl, $dent, $lineno); 1171 next; 1172 } 1173 } 1174 } 1175 1176 my @paraminfo = (); 1177 my $rettype = undef; 1178 my @decllines = ( $decl ); 1179 my $sym = ''; 1180 1181 if ($symtype == -1) { # category documentation with no symbol attached. 1182 @decllines = (); 1183 if ($str =~ /^#\s*Category(.*?)\s*$/m) { 1184 $sym = "[category documentation] $1"; # make a fake, unique symbol that's not valid C. 1185 } else { 1186 die("Unexpected category documentation line '$str' in '$incpath/$dent' ...?"); 1187 } 1188 $headercategorydocs{$current_wiki_category} = $sym; 1189 } elsif ($symtype == 1) { # a function 1190 my $is_forced_inline = ($decl =~ /\A\s*SDL_FORCE_INLINE/); 1191 1192 if ($is_forced_inline) { 1193 if (not $decl =~ /\)\s*(\{.*|)\s*\Z/) { 1194 while (<FH>) { 1195 chomp; 1196 $lineno++; 1197 push @decllines, $_; 1198 s/\A\s+//; 1199 s/\s+\Z//; 1200 $decl .= " $_"; 1201 last if /\)\s*(\{.*|)\s*\Z/; 1202 } 1203 } 1204 $decl =~ s/\s*\)\s*(\{.*|)\s*\Z/);/; 1205 } else { 1206 if (not $decl =~ /;/) { 1207 while (<FH>) { 1208 chomp; 1209 $lineno++; 1210 push @decllines, $_; 1211 s/\A\s+//; 1212 s/\s+\Z//; 1213 $decl .= " $_"; 1214 last if /;/; 1215 } 1216 } 1217 $decl =~ s/\s+\);\Z/);/; 1218 $decl =~ s/\s+;\Z/;/; 1219 } 1220 1221 $decl =~ s/\s+\Z//; 1222 1223 $decl = strip_fn_declaration_metadata($decl); 1224 1225 my $paramsstr = undef; 1226 1227 if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) { 1228 $sym = $8; 1229 $rettype = "$3$4$5$6"; 1230 $paramsstr = $9; 1231 } elsif ($is_forced_inline && $decl =~ /\A\s*SDL_FORCE_INLINE\s+(SDL_DEPRECATED\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/) { 1232 $sym = $6; 1233 $rettype = "$2$3$4$5"; 1234 $paramsstr = $7; 1235 } else { 1236 #print "Found doxygen but no function sig:\n$str\n\n"; 1237 foreach (@templines) { 1238 push @contents, $_; 1239 } 1240 foreach (@decllines) { 1241 push @contents, $_; 1242 } 1243 next; 1244 } 1245 1246 $rettype = sanitize_c_typename($rettype); 1247 1248 if ($paramsstr =~ /\(/) { 1249 die("\n\n$0 FAILURE!\n" . 1250 "There's a '(' in the parameters for function '$sym' '$incpath/$dent'.\n" . 1251 "This usually means there's a parameter that's a function pointer type.\n" . 1252 "This causes problems for wikiheaders.pl and is less readable, too.\n" . 1253 "Please put that function pointer into a typedef,\n" . 1254 "and use the new type in this function's signature instead!\n\n"); 1255 } 1256 1257 my @params = split(/,/, $paramsstr); 1258 my $dotdotdot = 0; 1259 foreach (@params) { 1260 my $p = $_; 1261 $p =~ s/\A\s+//; 1262 $p =~ s/\s+\Z//; 1263 if (($p eq 'void') || ($p eq '')) { 1264 die("Void parameter in a function with multiple params?! ('$sym' in '$incpath/$dent')") if (scalar(@params) != 1); 1265 } elsif ($p eq '...') { 1266 die("Mutiple '...' params?! ('$sym' in '$incpath/$dent')") if ($dotdotdot); 1267 $dotdotdot = 1; 1268 push @paraminfo, '...'; 1269 push @paraminfo, '...'; 1270 } elsif ($p =~ /\A(.*)\s+([a-zA-Z0-9_\*\[\]]+)\Z/) { 1271 die("Parameter after '...' param?! ('$sym' in '$incpath/$dent')") if ($dotdotdot); 1272 my $t = $1; 1273 my $n = $2; 1274 if ($n =~ s/\A(\*+)//) { 1275 $t .= $1; # move any `*` that stuck to the name over. 1276 } 1277 if ($n =~ s/\[\]\Z//) { 1278 $t = "$t*"; # move any `[]` that stuck to the name over, as a pointer. 1279 } 1280 $t = sanitize_c_typename($t); 1281 #print("$t\n"); 1282 #print("$n\n"); 1283 push @paraminfo, $n; 1284 push @paraminfo, $t; 1285 } else { 1286 die("Unexpected parameter '$p' in function '$sym' in '$incpath/$dent'!"); 1287 } 1288 } 1289 1290 if (!$is_forced_inline) { # don't do with forced-inline because we don't want the implementation inserted in the wiki. 1291 my $shrink_length = 0; 1292 1293 $decl = ''; # rebuild this with the line breaks, since it looks better for syntax highlighting. 1294 foreach (@decllines) { 1295 if ($decl eq '') { 1296 my $temp; 1297 1298 $decl = $_; 1299 $temp = $decl; 1300 $temp =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 /; 1301 $shrink_length = length($decl) - length($temp); 1302 $decl = $temp; 1303 } else { 1304 my $trimmed = $_; 1305 $trimmed =~ s/\A\s{$shrink_length}//; # shrink to match the removed "extern SDL_DECLSPEC SDLCALL " 1306 $decl .= $trimmed; 1307 } 1308 $decl .= "\n"; 1309 } 1310 } 1311 1312 $decl = strip_fn_declaration_metadata($decl); 1313 1314 # !!! FIXME: code duplication with typedef processing, below. 1315 # We assume any `#define`s directly after the function are related to it: probably bitflags for an integer typedef. 1316 # We'll also allow some other basic preprocessor lines. 1317 # Blank lines are allowed, anything else, even comments, are not. 1318 my $blank_lines = 0; 1319 my $lastpos = tell(FH); 1320 my $lastlineno = $lineno; 1321 my $additional_decl = ''; 1322 my $saw_define = 0; 1323 while (<FH>) { 1324 chomp; 1325 1326 $lineno++; 1327 1328 if (/\A\s*\Z/) { 1329 $blank_lines++; 1330 } elsif (/\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/) { 1331 if (/\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/) { 1332 $referenceonly{$1} = $sym; 1333 $saw_define = 1; 1334 } elsif (!$saw_define) { 1335 # if the first non-blank thing isn't a #define, assume we're done. 1336 seek(FH, $lastpos, 0); # re-read eaten lines again next time. 1337 $lineno = $lastlineno; 1338 last; 1339 } 1340 1341 # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text. 1342 1343 # At Sam's request, don't list property defines with functions. (See #9440) 1344 my $is_property = /\A\s*\#\s*define\s+SDL_PROP_/; 1345 if (!$is_property) { 1346 if ($blank_lines > 0) { 1347 while ($blank_lines > 0) { 1348 $additional_decl .= "\n"; 1349 push @decllines, ''; 1350 $blank_lines--; 1351 } 1352 } 1353 $additional_decl .= "\n$_"; 1354 push @decllines, $_; 1355 $lastpos = tell(FH); 1356 } 1357 } else { 1358 seek(FH, $lastpos, 0); # re-read eaten lines again next time. 1359 $lineno = $lastlineno; 1360 last; 1361 } 1362 } 1363 $decl .= $additional_decl; 1364 } elsif ($symtype == 2) { # a macro 1365 if ($decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)\s+/) { 1366 $sym = $1; 1367 } else { 1368 #print "Found doxygen but no macro:\n$str\n\n"; 1369 foreach (@templines) { 1370 push @contents, $_; 1371 } 1372 foreach (@decllines) { 1373 push @contents, $_; 1374 } 1375 next; 1376 } 1377 1378 while ($decl =~ /\\\Z/) { 1379 my $l = <FH>; 1380 last if not $l; 1381 $lineno++; 1382 chomp($l); 1383 push @decllines, $l; 1384 #$l =~ s/\A\s+//; 1385 $l =~ s/\s+\Z//; 1386 $decl .= "\n$l"; 1387 } 1388 } elsif (($symtype == 3) || ($symtype == 4)) { # struct or union or enum 1389 my $has_definition = 0; 1390 if ($decl =~ /\A\s*(typedef\s+|)(struct|union|enum)\s*([a-zA-Z0-9_]*?)\s*(\n|\{|\;|\Z)/) { 1391 my $ctype = $2; 1392 my $origsym = $3; 1393 my $ending = $4; 1394 $sym = $origsym; 1395 if ($sym =~ s/\A(.*?)(\s+)(.*?)\Z/$1/) { 1396 die("Failed to parse '$origsym' correctly!") if ($sym ne $1); # Thought this was "typedef struct MySym MySym;" ... it was not. :( This is a hack! 1397 } 1398 if ($sym eq '') { 1399 die("\n\n$0 FAILURE!\n" . 1400 "There's a 'typedef $ctype' in $incpath/$dent without a name at the top.\n" . 1401 "Instead of `typedef $ctype {} x;`, this should be `typedef $ctype x {} x;`.\n" . 1402 "This causes problems for wikiheaders.pl and scripting language bindings.\n" . 1403 "Please fix it!\n\n"); 1404 } 1405 $has_definition = ($ending ne ';'); 1406 } else { 1407 #print "Found doxygen but no datatype:\n$str\n\n"; 1408 foreach (@templines) { 1409 push @contents, $_; 1410 } 1411 foreach (@decllines) { 1412 push @contents, $_; 1413 } 1414 next; 1415 } 1416 1417 # This block attempts to find the whole struct/union/enum definition by counting matching brackets. Kind of yucky. 1418 # It also "parses" enums enough to find out the elements of it. 1419 if ($has_definition) { 1420 my $started = 0; 1421 my $brackets = 0; 1422 my $pending = $decl; 1423 my $skipping_comment = 0; 1424 1425 $decl = ''; 1426 while (!$started || ($brackets != 0)) { 1427 foreach my $seg (split(/([{}])/, $pending)) { # (this will pick up brackets in comments! Be careful!) 1428 $decl .= $seg; 1429 if ($seg eq '{') { 1430 $started = 1; 1431 $brackets++; 1432 } elsif ($seg eq '}') { 1433 die("Something is wrong with header $incpath/$dent while parsing $sym; is a bracket missing?\n") if ($brackets <= 0); 1434 $brackets--; 1435 } 1436 } 1437 1438 if ($skipping_comment) { 1439 if ($pending =~ s/\A.*?\*\///) { 1440 $skipping_comment = 0; 1441 } 1442 } 1443 1444 if (!$skipping_comment && $started && ($symtype == 4)) { # Pick out elements of an enum. 1445 my $stripped = "$pending"; 1446 $stripped =~ s/\/\*.*?\*\///g; # dump /* comments */ that exist fully on one line. 1447 if ($stripped =~ /\/\*/) { # uhoh, a /* comment */ that crosses newlines. 1448 $skipping_comment = 1; 1449 } elsif ($stripped =~ /\A\s*([a-zA-Z0-9_]+)(.*)\Z/) { #\s*(\=\s*.*?|)\s*,?(.*?)\Z/) { 1450 if ($1 ne 'typedef') { # make sure we didn't just eat the first line by accident. :/ 1451 #print("ENUM [$1] $incpath/$dent:$lineno\n"); 1452 $referenceonly{$1} = $sym; 1453 } 1454 } 1455 } 1456 1457 if (!$started || ($brackets != 0)) { 1458 $pending = <FH>; 1459 die("EOF/error reading $incpath/$dent while parsing $sym\n") if not $pending; 1460 $lineno++; 1461 chomp($pending); 1462 push @decllines, $pending; 1463 $decl .= "\n"; 1464 } 1465 } 1466 # 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! 1467 } 1468 } elsif ($symtype == 5) { # other typedef 1469 if ($decl =~ /\A\s*typedef\s+(.*)\Z/) { 1470 my $tdstr = $1; 1471 1472 if (not $decl =~ /;/) { 1473 while (<FH>) { 1474 chomp; 1475 $lineno++; 1476 push @decllines, $_; 1477 s/\A\s+//; 1478 s/\s+\Z//; 1479 $decl .= " $_"; 1480 last if /;/; 1481 } 1482 } 1483 $decl =~ s/\s+(\))?;\Z/$1;/; 1484 1485 $tdstr =~ s/;\s*\Z//; 1486 1487 #my $datatype; 1488 if ($tdstr =~ /\A(.*?)\s*\((.*?)\s*\*\s*(.*?)\)\s*\((.*?)(\))?/) { # a function pointer type 1489 $sym = $3; 1490 #$datatype = "$1 ($2 *$sym)($4)"; 1491 } elsif ($tdstr =~ /\A(.*[\s\*]+)(.*?)\s*\Z/) { 1492 $sym = $2; 1493 #$datatype = $1; 1494 } else { 1495 die("Failed to parse typedef '$tdstr' in $incpath/$dent!\n"); # I'm hitting a C grammar nail with a regexp hammer here, y'all. 1496 } 1497 1498 $sym =~ s/\A\s+//; 1499 $sym =~ s/\s+\Z//; 1500 #$datatype =~ s/\A\s+//; 1501 #$datatype =~ s/\s+\Z//; 1502 } else { 1503 #print "Found doxygen but no datatype:\n$str\n\n"; 1504 foreach (@templines) { 1505 push @contents, $_; 1506 } 1507 foreach (@decllines) { 1508 push @contents, $_; 1509 } 1510 next; 1511 } 1512 1513 # We assume any `#define`s directly after the typedef are related to it: probably bitflags for an integer typedef. 1514 # We'll also allow some other basic preprocessor lines. 1515 # Blank lines are allowed, anything else, even comments, are not. 1516 my $blank_lines = 0; 1517 my $lastpos = tell(FH); 1518 my $lastlineno = $lineno; 1519 my $additional_decl = ''; 1520 my $saw_define = 0; 1521 while (<FH>) { 1522 chomp; 1523 1524 $lineno++; 1525 1526 if (/\A\s*\Z/) { 1527 $blank_lines++; 1528 } elsif (/\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/) { 1529 if (/\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/) { 1530 $referenceonly{$1} = $sym; 1531 $saw_define = 1; 1532 } elsif (!$saw_define) { 1533 # if the first non-blank thing isn't a #define, assume we're done. 1534 seek(FH, $lastpos, 0); # re-read eaten lines again next time. 1535 $lineno = $lastlineno; 1536 last; 1537 } 1538 # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text. 1539 if ($blank_lines > 0) { 1540 while ($blank_lines > 0) { 1541 $additional_decl .= "\n"; 1542 push @decllines, ''; 1543 $blank_lines--; 1544 } 1545 } 1546 $additional_decl .= "\n$_"; 1547 push @decllines, $_; 1548 $lastpos = tell(FH); 1549 } else { 1550 seek(FH, $lastpos, 0); # re-read eaten lines again next time. 1551 $lineno = $lastlineno; 1552 last; 1553 } 1554 } 1555 $decl .= $additional_decl; 1556 } else { 1557 die("Unexpected symtype $symtype"); 1558 } 1559 1560 #print("DECL: [$decl]\n"); 1561 1562 #print("$sym:\n$str\n\n"); 1563 1564 # There might be multiple declarations of a function due to #ifdefs, 1565 # and only one of them will have documentation. If we hit an 1566 # undocumented one before, delete the placeholder line we left for 1567 # it so it doesn't accumulate a new blank line on each run. 1568 my $skipsym = 0; 1569 if (defined $headersymshasdoxygen{$sym}) { 1570 if ($headersymshasdoxygen{$sym} == 0) { # An undocumented declaration already exists, nuke its placeholder line. 1571 delete $contents[$headersymschunk{$sym}]; # delete DOES NOT RENUMBER existing elements! 1572 } else { # documented function already existed? 1573 $skipsym = 1; # don't add this copy to the list of functions. 1574 if ($has_doxygen) { 1575 print STDERR "WARNING: Symbol '$sym' appears to be documented in multiple locations. Only keeping the first one we saw!\n"; 1576 } 1577 push @contents, join("\n", @decllines) if (scalar(@decllines) > 0); # just put the existing declation in as-is. 1578 } 1579 } 1580 1581 if (!$skipsym) { 1582 $headersymscategory{$sym} = $current_wiki_category if defined $current_wiki_category; 1583 $headersyms{$sym} = $str; 1584 $headerdecls{$sym} = $decl; 1585 $headersymslocation{$sym} = $dent; 1586 $headersymschunk{$sym} = scalar(@contents); 1587 $headersymshasdoxygen{$sym} = $has_doxygen; 1588 $headersymstype{$sym} = $symtype; 1589 $headersymsparaminfo{$sym} = \@paraminfo if (scalar(@paraminfo) > 0); 1590 $headersymsrettype{$sym} = $rettype if (defined($rettype)); 1591 push @function_order, $sym if ($symtype == 1) || ($symtype == 2); 1592 push @contents, join("\n", @templines); 1593 push @contents, join("\n", @decllines) if (scalar(@decllines) > 0); 1594 } 1595 1596 } 1597 close(FH); 1598 1599 $headers{$dent} = \@contents; 1600 $quickreffuncorder{$current_wiki_category} = \@function_order if defined $current_wiki_category; 1601} 1602closedir(DH); 1603 1604 1605opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n"); 1606while (my $d = readdir(DH)) { 1607 my $dent = $d; 1608 my $type = ''; 1609 if ($dent =~ /\.(md|mediawiki)\Z/) { 1610 $type = $1; 1611 } else { 1612 next; # only dealing with wiki pages. 1613 } 1614 1615 my $sym = $dent; 1616 $sym =~ s/\..*\Z//; 1617 1618 # (There are other pages to ignore, but these are known ones to not bother parsing.) 1619 # Ignore FrontPage. 1620 next if $sym eq 'FrontPage'; 1621 1622 open(FH, '<', "$wikipath/$dent") or die("Can't open '$wikipath/$dent': $!\n"); 1623 1624 if ($sym =~ /\ACategory(.*?)\Z/) { # Special case for Category pages. 1625 # Find the end of the category documentation in the existing file and append everything else to the new file. 1626 my $cat = $1; 1627 my $docstr = ''; 1628 my $notdocstr = ''; 1629 my $docs = 1; 1630 while (<FH>) { 1631 chomp; 1632 if ($docs) { 1633 $docs = 0 if /\A\-\-\-\-\Z/; # Hit a footer? We're done. 1634 $docs = 0 if /\A<!\-\-/; # Hit an HTML comment? We're done. 1635 } 1636 if ($docs) { 1637 $docstr .= "$_\n"; 1638 } else { 1639 $notdocstr .= "$_\n"; 1640 } 1641 } 1642 close(FH); 1643 1644 $docstr =~ s/\s*\Z//; 1645 1646 $sym = "[category documentation] $cat"; # make a fake, unique symbol that's not valid C. 1647 $wikitypes{$sym} = $type; 1648 my %sections = (); 1649 $sections{'Remarks'} = $docstr; 1650 $sections{'[footer]'} = $notdocstr; 1651 $wikisyms{$sym} = \%sections; 1652 my @section_order = ( 'Remarks', '[footer]' ); 1653 $wikisectionorder{$sym} = \@section_order; 1654 next; 1655 } 1656 1657 my $current_section = '[start]'; 1658 my @section_order = ( $current_section ); 1659 my %sections = (); 1660 $sections{$current_section} = ''; 1661 1662 my $firstline = 1; 1663 1664 while (<FH>) { 1665 chomp; 1666 my $orig = $_; 1667 s/\A\s*//; 1668 s/\s*\Z//; 1669 1670 if ($type eq 'mediawiki') { 1671 if (defined($wikipreamble) && $firstline && /\A\=\=\=\=\=\= (.*?) \=\=\=\=\=\=\Z/ && ($1 eq $wikipreamble)) { 1672 $firstline = 0; # skip this. 1673 next; 1674 } elsif (/\A\= (.*?) \=\Z/) { 1675 $firstline = 0; 1676 $current_section = ($1 eq $sym) ? '[Brief]' : $1; 1677 die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section}; 1678 push @section_order, $current_section; 1679 $sections{$current_section} = ''; 1680 } elsif (/\A\=\= (.*?) \=\=\Z/) { 1681 $firstline = 0; 1682 $current_section = ($1 eq $sym) ? '[Brief]' : $1; 1683 die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section}; 1684 push @section_order, $current_section; 1685 $sections{$current_section} = ''; 1686 next; 1687 } elsif (/\A\-\-\-\-\Z/) { 1688 $firstline = 0; 1689 $current_section = '[footer]'; 1690 die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section}; 1691 push @section_order, $current_section; 1692 $sections{$current_section} = ''; 1693 next; 1694 } 1695 } elsif ($type eq 'md') { 1696 if (defined($wikipreamble) && $firstline && /\A\#\#\#\#\#\# (.*?)\Z/ && ($1 eq $wikipreamble)) { 1697 $firstline = 0; # skip this. 1698 next; 1699 } elsif (/\A\#+ (.*?)\Z/) { 1700 $firstline = 0; 1701 $current_section = ($1 eq $sym) ? '[Brief]' : $1; 1702 die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section}; 1703 push @section_order, $current_section; 1704 $sections{$current_section} = ''; 1705 next; 1706 } elsif (/\A\-\-\-\-\Z/) { 1707 $firstline = 0; 1708 $current_section = '[footer]'; 1709 die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section}; 1710 push @section_order, $current_section; 1711 $sections{$current_section} = ''; 1712 next; 1713 } 1714 } else { 1715 die("Unexpected wiki file type. Fixme!"); 1716 } 1717 1718 if ($firstline) { 1719 $firstline = ($_ ne ''); 1720 } 1721 if (!$firstline) { 1722 $sections{$current_section} .= "$orig\n"; 1723 } 1724 } 1725 close(FH); 1726 1727 foreach (keys %sections) { 1728 $sections{$_} =~ s/\A\n+//; 1729 $sections{$_} =~ s/\n+\Z//; 1730 $sections{$_} .= "\n"; 1731 } 1732 1733 # older section name we used, migrate over from it. 1734 if (defined $sections{'Related Functions'}) { 1735 if (not defined $sections{'See Also'}) { 1736 $sections{'See Also'} = $sections{'Related Functions'}; 1737 } 1738 delete $sections{'Related Functions'}; 1739 } 1740 1741 if (0) { 1742 foreach (@section_order) { 1743 print("$sym SECTION '$_':\n"); 1744 print($sections{$_}); 1745 print("\n\n"); 1746 } 1747 } 1748 1749 $wikitypes{$sym} = $type; 1750 $wikisyms{$sym} = \%sections; 1751 $wikisectionorder{$sym} = \@section_order; 1752} 1753closedir(DH); 1754 1755delete $wikisyms{"Undocumented"}; 1756 1757{ 1758 my $path = "$wikipath/Undocumented.md"; 1759 open(my $fh, '>', $path) or die("Can't open '$path': $!\n"); 1760 1761 print $fh "# Undocumented\n\n"; 1762 print_undocumented_section($fh, 'Functions', 1); 1763 #print_undocumented_section($fh, 'Macros', 2); 1764 1765 close($fh); 1766} 1767 1768if ($warn_about_missing) { 1769 foreach (keys %wikisyms) { 1770 my $sym = $_; 1771 if (not defined $headersyms{$sym}) { 1772 print STDERR "WARNING: $sym defined in the wiki but not the headers!\n"; 1773 } 1774 } 1775 1776 foreach (keys %headersyms) { 1777 my $sym = $_; 1778 if (not defined $wikisyms{$sym}) { 1779 print STDERR "WARNING: $sym defined in the headers but not the wiki!\n"; 1780 } 1781 } 1782} 1783 1784if ($copy_direction == 1) { # --copy-to-headers 1785 my %changed_headers = (); 1786 1787 $dewikify_mode = 'md'; 1788 $wordwrap_mode = 'md'; # the headers use Markdown format. 1789 1790 foreach (keys %headersyms) { 1791 my $sym = $_; 1792 next if not defined $wikisyms{$sym}; # don't have a page for that function, skip it. 1793 my $symtype = $headersymstype{$sym}; 1794 my $wikitype = $wikitypes{$sym}; 1795 my $sectionsref = $wikisyms{$sym}; 1796 my $remarks = $sectionsref->{'Remarks'}; 1797 my $returns = $sectionsref->{'Return Value'}; 1798 my $threadsafety = $sectionsref->{'Thread Safety'}; 1799 my $version = $sectionsref->{'Version'}; 1800 my $related = $sectionsref->{'See Also'}; 1801 my $deprecated = $sectionsref->{'Deprecated'}; 1802 my $brief = $sectionsref->{'[Brief]'}; 1803 my $addblank = 0; 1804 my $str = ''; 1805 1806 my $params = undef; 1807 my $paramstr = undef; 1808 1809 if ($symtype == -1) { # category documentation block. 1810 # nothing to be done here. 1811 } elsif (($symtype == 1) || (($symtype == 5))) { # we'll assume a typedef (5) with a \param is a function pointer typedef. 1812 $params = $sectionsref->{'Function Parameters'}; 1813 $paramstr = '\param'; 1814 } elsif ($symtype == 2) { 1815 $params = $sectionsref->{'Macro Parameters'}; 1816 $paramstr = '\param'; 1817 } elsif ($symtype == 3) { 1818 $params = $sectionsref->{'Fields'}; 1819 $paramstr = '\field'; 1820 } elsif ($symtype == 4) { 1821 $params = $sectionsref->{'Values'}; 1822 $paramstr = '\value'; 1823 } else { 1824 die("Unexpected symtype $symtype"); 1825 } 1826 1827 $headersymshasdoxygen{$sym} = 1; # Added/changed doxygen for this header. 1828 1829 $brief = dewikify($wikitype, $brief); 1830 $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary. 1831 my @briefsplit = split /\n/, $brief; 1832 $brief = shift @briefsplit; 1833 1834 if (defined $remarks) { 1835 $remarks = join("\n", @briefsplit) . dewikify($wikitype, $remarks); 1836 } 1837 1838 if (defined $brief) { 1839 $str .= "\n" if $addblank; $addblank = 1; 1840 $str .= wordwrap($brief) . "\n"; 1841 } 1842 1843 if (defined $remarks) { 1844 $str .= "\n" if $addblank; $addblank = 1; 1845 $str .= wordwrap($remarks) . "\n"; 1846 } 1847 1848 if (defined $deprecated) { 1849 # !!! FIXME: lots of code duplication in all of these. 1850 $str .= "\n" if $addblank; $addblank = 1; 1851 my $v = dewikify($wikitype, $deprecated); 1852 my $whitespacelen = length("\\deprecated") + 1; 1853 my $whitespace = ' ' x $whitespacelen; 1854 $v = wordwrap($v, -$whitespacelen); 1855 my @desclines = split /\n/, $v; 1856 my $firstline = shift @desclines; 1857 $str .= "\\deprecated $firstline\n"; 1858 foreach (@desclines) { 1859 $str .= "${whitespace}$_\n"; 1860 } 1861 } 1862 1863 if (defined $params) { 1864 $str .= "\n" if $addblank; $addblank = (defined $returns) ? 0 : 1; 1865 my @lines = split /\n/, dewikify($wikitype, $params); 1866 if ($wikitype eq 'mediawiki') { 1867 die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start 1868 while (scalar(@lines) >= 3) { 1869 my $c_datatype = shift @lines; 1870 my $name = shift @lines; 1871 my $desc = shift @lines; 1872 my $terminator; # the '|-' or '|}' line. 1873 1874 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. 1875 $terminator = $desc; 1876 $desc = $name; 1877 $name = $c_datatype; 1878 $c_datatype = ''; 1879 } else { 1880 $terminator = shift @lines; 1881 } 1882 1883 last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table. 1884 $name =~ s/\A\|\s*//; 1885 $name =~ s/\A\*\*(.*?)\*\*/$1/; 1886 $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/; 1887 $desc =~ s/\A\|\s*//; 1888 #print STDERR "SYM: $sym CDATATYPE: $c_datatype NAME: $name DESC: $desc TERM: $terminator\n"; 1889 my $whitespacelen = length($name) + 8; 1890 my $whitespace = ' ' x $whitespacelen; 1891 $desc = wordwrap($desc, -$whitespacelen); 1892 my @desclines = split /\n/, $desc; 1893 my $firstline = shift @desclines; 1894 $str .= "$paramstr $name $firstline\n"; 1895 foreach (@desclines) { 1896 $str .= "${whitespace}$_\n"; 1897 } 1898 } 1899 } elsif ($wikitype eq 'md') { 1900 my $l; 1901 $l = shift @lines; 1902 die("Unexpected data parsing Markdown table") if (not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/); 1903 $l = shift @lines; 1904 die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/); 1905 while (scalar(@lines) >= 1) { 1906 $l = shift @lines; 1907 my $name; 1908 my $desc; 1909 if ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) { 1910 # c datatype is $1, but we don't care about it here. 1911 $name = $2; 1912 $desc = $3; 1913 } elsif ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) { 1914 $name = $1; 1915 $desc = $2; 1916 } else { 1917 last; # we seem to have run out of table. 1918 } 1919 1920 $name =~ s/\A\*\*(.*?)\*\*/$1/; 1921 $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/; 1922 #print STDERR "SYM: $sym NAME: $name DESC: $desc\n"; 1923 my $whitespacelen = length($name) + 8; 1924 my $whitespace = ' ' x $whitespacelen; 1925 $desc = wordwrap($desc, -$whitespacelen); 1926 my @desclines = split /\n/, $desc; 1927 my $firstline = shift @desclines; 1928 $str .= "$paramstr $name $firstline\n"; 1929 foreach (@desclines) { 1930 $str .= "${whitespace}$_\n"; 1931 } 1932 } 1933 } else { 1934 die("write me"); 1935 } 1936 } 1937 1938 if (defined $returns) { 1939 $str .= "\n" if $addblank; $addblank = 1; 1940 my $r = dewikify($wikitype, $returns); 1941 $r =~ s/\A\(.*?\)\s*//; # Chop datatype in parentheses off the front. 1942 my $retstr = "\\returns"; 1943 if ($r =~ s/\AReturn(s?)\s+//) { 1944 $retstr = "\\return$1"; 1945 } 1946 1947 my $whitespacelen = length($retstr) + 1; 1948 my $whitespace = ' ' x $whitespacelen; 1949 $r = wordwrap($r, -$whitespacelen); 1950 my @desclines = split /\n/, $r; 1951 my $firstline = shift @desclines; 1952 $str .= "$retstr $firstline\n"; 1953 foreach (@desclines) { 1954 $str .= "${whitespace}$_\n"; 1955 } 1956 } 1957 1958 if (defined $threadsafety) { 1959 # !!! FIXME: lots of code duplication in all of these. 1960 $str .= "\n" if $addblank; $addblank = 1; 1961 my $v = dewikify($wikitype, $threadsafety); 1962 my $whitespacelen = length("\\threadsafety") + 1; 1963 my $whitespace = ' ' x $whitespacelen; 1964 $v = wordwrap($v, -$whitespacelen); 1965 my @desclines = split /\n/, $v; 1966 my $firstline = shift @desclines; 1967 $str .= "\\threadsafety $firstline\n"; 1968 foreach (@desclines) { 1969 $str .= "${whitespace}$_\n"; 1970 } 1971 } 1972 1973 if (defined $version) { 1974 # !!! FIXME: lots of code duplication in all of these. 1975 $str .= "\n" if $addblank; $addblank = 1; 1976 my $v = dewikify($wikitype, $version); 1977 my $whitespacelen = length("\\since") + 1; 1978 my $whitespace = ' ' x $whitespacelen; 1979 $v = wordwrap($v, -$whitespacelen); 1980 my @desclines = split /\n/, $v; 1981 my $firstline = shift @desclines; 1982 $str .= "\\since $firstline\n"; 1983 foreach (@desclines) { 1984 $str .= "${whitespace}$_\n"; 1985 } 1986 } 1987 1988 if (defined $related) { 1989 # !!! FIXME: lots of code duplication in all of these. 1990 $str .= "\n" if $addblank; $addblank = 1; 1991 my $v = dewikify($wikitype, $related); 1992 my @desclines = split /\n/, $v; 1993 foreach (@desclines) { 1994 s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func" 1995 s/\[\[(.*?)\]\]/$1/; # in case some wikilinks remain. 1996 s/\[(.*?)\]\(.*?\)/$1/; # in case some wikilinks remain. 1997 s/\A\/*//; 1998 s/\A\s*[\:\*\-]\s*//; 1999 s/\A\s+//; 2000 s/\s+\Z//; 2001 $str .= "\\sa $_\n"; 2002 } 2003 } 2004 2005 my $header = $headersymslocation{$sym}; 2006 my $contentsref = $headers{$header}; 2007 my $chunk = $headersymschunk{$sym}; 2008 2009 my @lines = split /\n/, $str; 2010 2011 my $addnewline = (($chunk > 0) && ($$contentsref[$chunk-1] ne '')) ? "\n" : ''; 2012 2013 my $output = "$addnewline/**\n"; 2014 foreach (@lines) { 2015 chomp; 2016 s/\s*\Z//; 2017 if ($_ eq '') { 2018 $output .= " *\n"; 2019 } else { 2020 $output .= " * $_\n"; 2021 } 2022 } 2023 $output .= " */"; 2024 2025 #print("$sym:\n[$output]\n\n"); 2026 2027 $$contentsref[$chunk] = $output; 2028 #$$contentsref[$chunk+1] = $headerdecls{$sym}; 2029 2030 $changed_headers{$header} = 1; 2031 } 2032 2033 foreach (keys %changed_headers) { 2034 my $header = $_; 2035 2036 # this is kinda inefficient, but oh well. 2037 my @removelines = (); 2038 foreach (keys %headersymslocation) { 2039 my $sym = $_; 2040 next if $headersymshasdoxygen{$sym}; 2041 next if $headersymslocation{$sym} ne $header; 2042 # 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. 2043 push @removelines, $headersymschunk{$sym}; 2044 } 2045 2046 my $contentsref = $headers{$header}; 2047 foreach (@removelines) { 2048 delete $$contentsref[$_]; # delete DOES NOT RENUMBER existing elements! 2049 } 2050 2051 my $path = "$incpath/$header.tmp"; 2052 open(FH, '>', $path) or die("Can't open '$path': $!\n"); 2053 foreach (@$contentsref) { 2054 print FH "$_\n" if defined $_; 2055 } 2056 close(FH); 2057 rename($path, "$incpath/$header") or die("Can't rename '$path' to '$incpath/$header': $!\n"); 2058 } 2059 2060 if (defined $readmepath) { 2061 if ( -d $wikireadmepath ) { 2062 mkdir($readmepath); # just in case 2063 opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n"); 2064 while (readdir(DH)) { 2065 my $dent = $_; 2066 if ($dent =~ /\A(.*?)\.md\Z/) { # we only bridge Markdown files here. 2067 next if $1 eq 'FrontPage'; 2068 filecopy("$wikireadmepath/$dent", "$readmepath/README-$dent", "\n"); 2069 } 2070 } 2071 closedir(DH); 2072 } 2073 } 2074 2075} elsif ($copy_direction == -1) { # --copy-to-wiki 2076 2077 my %briefs = (); # $briefs{'SDL_OpenAudio'} -> the \brief string for the function. 2078 2079 if (defined $changeformat) { 2080 $dewikify_mode = $changeformat; 2081 $wordwrap_mode = $changeformat; 2082 } 2083 2084 foreach (keys %headersyms) { 2085 my $sym = $_; 2086 next if not $headersymshasdoxygen{$sym}; 2087 next if $sym =~ /\A\[category documentation\]/; # not real symbols, we handle this elsewhere. 2088 my $symtype = $headersymstype{$sym}; 2089 my $origwikitype = defined $wikitypes{$sym} ? $wikitypes{$sym} : 'md'; # default to MarkDown for new stuff. 2090 my $wikitype = (defined $changeformat) ? $changeformat : $origwikitype; 2091 die("Unexpected wikitype '$wikitype'") if (($wikitype ne 'mediawiki') and ($wikitype ne 'md') and ($wikitype ne 'manpage')); 2092 2093 #print("$sym\n"); next; 2094 2095 $wordwrap_mode = $wikitype; 2096 2097 my $raw = $headersyms{$sym}; # raw doxygen text with comment characters stripped from start/end and start of each line. 2098 next if not defined $raw; 2099 $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. 2100 2101 my @doxygenlines = split /\n/, $raw; 2102 my $brief = ''; 2103 while (@doxygenlines) { 2104 last if $doxygenlines[0] =~ /\A\\/; # some sort of doxygen command, assume we're past the general remarks. 2105 last if $doxygenlines[0] =~ /\A\s*\Z/; # blank line? End of paragraph, done. 2106 my $l = shift @doxygenlines; 2107 chomp($l); 2108 $l =~ s/\A\s*//; 2109 $l =~ s/\s*\Z//; 2110 $brief .= "$l "; 2111 } 2112 2113 $brief =~ s/\s+\Z//; 2114 $brief =~ s/\A(.*?\.) /$1\n\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary. 2115 my @briefsplit = split /\n/, $brief; 2116 2117 next if not defined $briefsplit[0]; # No brief text? Probably a bogus Doxygen comment, skip it. 2118 2119 $brief = wikify($wikitype, shift @briefsplit) . "\n"; 2120 @doxygenlines = (@briefsplit, @doxygenlines); 2121 2122 my $remarks = ''; 2123 while (@doxygenlines) { 2124 last if $doxygenlines[0] =~ /\A\\/; # some sort of doxygen command, assume we're past the general remarks. 2125 my $l = shift @doxygenlines; 2126 $remarks .= "$l\n"; 2127 } 2128 2129 #print("REMARKS:\n\n $remarks\n\n"); 2130 2131 $remarks = wordwrap(wikify($wikitype, $remarks)); 2132 $remarks =~ s/\A\s*//; 2133 $remarks =~ s/\s*\Z//; 2134 2135 my $decl = $headerdecls{$sym}; 2136 2137 my $syntax = ''; 2138 if ($wikitype eq 'mediawiki') { 2139 $syntax = "<syntaxhighlight lang='c'>\n$decl</syntaxhighlight>\n"; 2140 } elsif ($wikitype eq 'md') { 2141 $decl =~ s/\n+\Z//; 2142 $syntax = "```c\n$decl\n```\n"; 2143 } else { die("Expected wikitype '$wikitype'"); } 2144 2145 my %sections = (); 2146 $sections{'[Brief]'} = $brief; # include this section even if blank so we get a title line. 2147 $sections{'Remarks'} = "$remarks\n" if $remarks ne ''; 2148 $sections{'Syntax'} = $syntax; 2149 2150 $briefs{$sym} = $brief; 2151 2152 my %params = (); # have to parse these and build up the wiki tables after, since Markdown needs to know the length of the largest string. :/ 2153 my @paramsorder = (); 2154 my $fnsigparams = $headersymsparaminfo{$sym}; 2155 my $has_returns = 0; 2156 my $has_threadsafety = 0; 2157 2158 while (@doxygenlines) { 2159 my $l = shift @doxygenlines; 2160 # We allow param/field/value interchangeably, even if it doesn't make sense. The next --copy-to-headers will correct it anyhow. 2161 if ($l =~ /\A\\(param|field|value)\s+(.*?)\s+(.*)\Z/) { 2162 my $arg = $2; 2163 my $desc = $3; 2164 while (@doxygenlines) { 2165 my $subline = $doxygenlines[0]; 2166 $subline =~ s/\A\s*//; 2167 last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing. 2168 shift @doxygenlines; # dump this line from the array; we're using it. 2169 if ($subline eq '') { # empty line, make sure it keeps the newline char. 2170 $desc .= "\n"; 2171 } else { 2172 $desc .= " $subline"; 2173 } 2174 } 2175 2176 $desc =~ s/[\s\n]+\Z//ms; 2177 2178 if (0) { # !!! FIXME: disabled because it's not currently suitable for general use, but for manually inspecting the output, it can be useful. 2179 if (($desc =~ /\A[A-Z]/) && (not $desc =~ /\ASDL_/)) { 2180 print STDERR "WARNING: $sym\'s '\\param $arg' text starts with a capital letter: '$desc'. Fixing.\n"; 2181 $desc = lcfirst($desc); 2182 } 2183 } 2184 2185 if (not $desc =~ /[\.\!]\Z/) { 2186 print STDERR "WARNING: $sym\'s '\\param $arg' text doesn't end with punctuation: '$desc'. Fixing.\n"; 2187 $desc .= '.'; 2188 } 2189 2190 # Validate this param. 2191 if (defined($params{$arg})) { 2192 print STDERR "WARNING: Symbol '$sym' has multiple '\\param $arg' declarations! Only keeping the first one!\n"; 2193 } elsif (defined $fnsigparams) { 2194 my $found = 0; 2195 for (my $i = 0; $i < scalar(@$fnsigparams); $i += 2) { 2196 $found = 1, last if (@$fnsigparams[$i] eq $arg); 2197 } 2198 if (!$found) { 2199 print STDERR "WARNING: Symbol '$sym' has a '\\param $arg' for a param that doesn't exist. It will be removed!\n"; 2200 } 2201 } 2202 2203 # We need to know the length of the longest string to make Markdown tables, so we just store these off until everything is parsed. 2204 $params{$arg} = $desc; 2205 push @paramsorder, $arg; 2206 } elsif ($l =~ /\A\\r(eturns?)\s+(.*)\Z/) { 2207 $has_returns = 1; 2208 # !!! FIXME: complain if this isn't a function or macro. 2209 my $retstr = "R$1"; # "Return" or "Returns" 2210 my $desc = $2; 2211 2212 while (@doxygenlines) { 2213 my $subline = $doxygenlines[0]; 2214 $subline =~ s/\A\s*//; 2215 last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing. 2216 shift @doxygenlines; # dump this line from the array; we're using it. 2217 if ($subline eq '') { # empty line, make sure it keeps the newline char. 2218 $desc .= "\n"; 2219 } else { 2220 $desc .= " $subline"; 2221 } 2222 } 2223 $desc =~ s/[\s\n]+\Z//ms; 2224 2225 if (0) { # !!! FIXME: disabled because it's not currently suitable for general use, but for manually inspecting the output, it can be useful. 2226 if (($desc =~ /\A[A-Z]/) && (not $desc =~ /\ASDL_/)) { 2227 print STDERR "WARNING: $sym\'s '\\returns' text starts with a capital letter: '$desc'. Fixing.\n"; 2228 $desc = lcfirst($desc); 2229 } 2230 } 2231 2232 if (not $desc =~ /[\.\!]\Z/) { 2233 print STDERR "WARNING: $sym\'s '\\returns' text doesn't end with punctuation: '$desc'. Fixing.\n"; 2234 $desc .= '.'; 2235 } 2236 2237 # Make sure the \returns info is valid. 2238 my $rettype = $headersymsrettype{$sym}; 2239 die("Don't have a rettype for '$sym' for some reason!") if (($symtype == 1) && (not defined($rettype))); 2240 if (defined($sections{'Return Value'})) { 2241 print STDERR "WARNING: Symbol '$sym' has multiple '\\return' declarations! Only keeping the first one!\n"; 2242 } elsif (($symtype != 1) && ($symtype != 2) && ($symtype != 5)) { # !!! FIXME: if 5, make sure it's a function pointer typedef! 2243 print STDERR "WARNING: Symbol '$sym' has a '\\return' declaration but isn't a function or macro! Removing it!\n"; 2244 } elsif (($symtype == 1) && ($headersymsrettype{$sym} eq 'void')) { 2245 print STDERR "WARNING: Function '$sym' has a '\\returns' declaration but function returns void! Removing it!\n"; 2246 } else { 2247 my $rettypestr = defined($rettype) ? ('(' . wikify($wikitype, $rettype) . ') ') : ''; 2248 $sections{'Return Value'} = wordwrap("$rettypestr$retstr ". wikify($wikitype, $desc)) . "\n"; 2249 } 2250 } elsif ($l =~ /\A\\deprecated\s+(.*)\Z/) { 2251 my $desc = $1; 2252 while (@doxygenlines) { 2253 my $subline = $doxygenlines[0]; 2254 $subline =~ s/\A\s*//; 2255 last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing. 2256 shift @doxygenlines; # dump this line from the array; we're using it. 2257 if ($subline eq '') { # empty line, make sure it keeps the newline char. 2258 $desc .= "\n"; 2259 } else { 2260 $desc .= " $subline"; 2261 } 2262 } 2263 $desc =~ s/[\s\n]+\Z//ms; 2264 $sections{'Deprecated'} = wordwrap(wikify($wikitype, $desc)) . "\n"; 2265 } elsif ($l =~ /\A\\since\s+(.*)\Z/) { 2266 my $desc = $1; 2267 while (@doxygenlines) { 2268 my $subline = $doxygenlines[0]; 2269 $subline =~ s/\A\s*//; 2270 last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing. 2271 shift @doxygenlines; # dump this line from the array; we're using it. 2272 if ($subline eq '') { # empty line, make sure it keeps the newline char. 2273 $desc .= "\n"; 2274 } else { 2275 $desc .= " $subline"; 2276 } 2277 } 2278 $desc =~ s/[\s\n]+\Z//ms; 2279 $sections{'Version'} = wordwrap(wikify($wikitype, $desc)) . "\n"; 2280 } elsif ($l =~ /\A\\threadsafety\s+(.*)\Z/) { 2281 my $desc = $1; 2282 while (@doxygenlines) { 2283 my $subline = $doxygenlines[0]; 2284 $subline =~ s/\A\s*//; 2285 last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing. 2286 shift @doxygenlines; # dump this line from the array; we're using it. 2287 if ($subline eq '') { # empty line, make sure it keeps the newline char. 2288 $desc .= "\n"; 2289 } else { 2290 $desc .= " $subline"; 2291 } 2292 } 2293 $desc =~ s/[\s\n]+\Z//ms; 2294 $sections{'Thread Safety'} = wordwrap(wikify($wikitype, $desc)) . "\n"; 2295 $has_threadsafety = 1; 2296 } elsif ($l =~ /\A\\sa\s+(.*)\Z/) { 2297 my $sa = $1; 2298 $sa =~ s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func" 2299 $sections{'See Also'} = '' if not defined $sections{'See Also'}; 2300 if ($wikitype eq 'mediawiki') { 2301 $sections{'See Also'} .= ":[[$sa]]\n"; 2302 } elsif ($wikitype eq 'md') { 2303 $sections{'See Also'} .= "- [$sa]($sa)\n"; 2304 } else { die("Expected wikitype '$wikitype'"); } 2305 } 2306 } 2307 2308 if (($symtype == 1) && ($headersymsrettype{$sym} ne 'void') && !$has_returns) { 2309 print STDERR "WARNING: Function '$sym' has a non-void return type but no '\\returns' declaration\n"; 2310 } 2311 2312 # !!! FIXME: uncomment this when we're trying to clean this up in the headers. 2313 #if (($symtype == 1) && !$has_threadsafety) { 2314 # print STDERR "WARNING: Function '$sym' doesn't have a '\\threadsafety' declaration\n"; 2315 #} 2316 2317 # Make sure %params is in the same order as the actual function signature and add C datatypes... 2318 my $params_has_c_datatype = 0; 2319 my @final_params = (); 2320 if (($symtype == 1) && (defined($headersymsparaminfo{$sym}))) { # is a function and we have param info for it... 2321 my $fnsigparams = $headersymsparaminfo{$sym}; 2322 for (my $i = 0; $i < scalar(@$fnsigparams); $i += 2) { 2323 my $paramname = @$fnsigparams[$i]; 2324 my $paramdesc = $params{$paramname}; 2325 if (defined($paramdesc)) { 2326 push @final_params, $paramname; # name 2327 push @final_params, @$fnsigparams[$i+1]; # C datatype 2328 push @final_params, $paramdesc; # description 2329 $params_has_c_datatype = 1 if (defined(@$fnsigparams[$i+1])); 2330 } else { 2331 print STDERR "WARNING: Symbol '$sym' is missing a '\\param $paramname' declaration!\n"; 2332 } 2333 } 2334 } else { 2335 foreach (@paramsorder) { 2336 my $paramname = $_; 2337 my $paramdesc = $params{$paramname}; 2338 if (defined($paramdesc)) { 2339 push @final_params, $_; 2340 push @final_params, undef; 2341 push @final_params, $paramdesc; 2342 } 2343 } 2344 } 2345 2346 my $hfiletext = $wikiheaderfiletext; 2347 $hfiletext =~ s/\%fname\%/$headersymslocation{$sym}/g; 2348 $sections{'Header File'} = "$hfiletext\n"; 2349 2350 # Make sure this ends with a double-newline. 2351 $sections{'See Also'} .= "\n" if defined $sections{'See Also'}; 2352 2353 if (0) { # !!! FIXME: this was a useful hack, but this needs to be generalized if we're going to do this always. 2354 # Plug in a \since section if one wasn't listed. 2355 if (not defined $sections{'Version'}) { 2356 my $symtypename; 2357 if ($symtype == 1) { 2358 $symtypename = 'function'; 2359 } elsif ($symtype == 2) { 2360 $symtypename = 'macro'; 2361 } elsif ($symtype == 3) { 2362 $symtypename = 'struct'; 2363 } elsif ($symtype == 4) { 2364 $symtypename = 'enum'; 2365 } elsif ($symtype == 5) { 2366 $symtypename = 'datatype'; 2367 } else { 2368 die("Unexpected symbol type $symtype!"); 2369 } 2370 my $str = "This $symtypename is available since SDL 3.0.0."; 2371 $sections{'Version'} = wordwrap(wikify($wikitype, $str)) . "\n"; 2372 } 2373 } 2374 2375 # We can build the wiki table now that we have all the data. 2376 if (scalar(@final_params) > 0) { 2377 my $str = ''; 2378 if ($wikitype eq 'mediawiki') { 2379 while (scalar(@final_params) > 0) { 2380 my $arg = shift @final_params; 2381 my $c_datatype = shift @final_params; 2382 my $desc = wikify($wikitype, shift @final_params); 2383 $c_datatype = '' if not defined $c_datatype; 2384 $str .= ($str eq '') ? "{|\n" : "|-\n"; 2385 $str .= "|$c_datatype\n" if $params_has_c_datatype; 2386 $str .= "|'''$arg'''\n"; 2387 $str .= "|$desc\n"; 2388 } 2389 $str .= "|}\n"; 2390 } elsif ($wikitype eq 'md') { 2391 my $longest_arg = 0; 2392 my $longest_c_datatype = 0; 2393 my $longest_desc = 0; 2394 my $which = 0; 2395 foreach (@final_params) { 2396 if ($which == 0) { 2397 my $len = length($_); 2398 $longest_arg = $len if ($len > $longest_arg); 2399 $which = 1; 2400 } elsif ($which == 1) { 2401 if (defined($_)) { 2402 my $len = length(wikify($wikitype, $_)); 2403 $longest_c_datatype = $len if ($len > $longest_c_datatype); 2404 } 2405 $which = 2; 2406 } else { 2407 my $len = length(wikify($wikitype, $_)); 2408 $longest_desc = $len if ($len > $longest_desc); 2409 $which = 0; 2410 } 2411 } 2412 2413 # Markdown tables are sort of obnoxious. 2414 my $c_datatype_cell; 2415 $c_datatype_cell = ($longest_c_datatype > 0) ? ('| ' . (' ' x ($longest_c_datatype)) . ' ') : ''; 2416 $str .= $c_datatype_cell . '| ' . (' ' x ($longest_arg+4)) . ' | ' . (' ' x $longest_desc) . " |\n"; 2417 $c_datatype_cell = ($longest_c_datatype > 0) ? ('| ' . ('-' x ($longest_c_datatype)) . ' ') : ''; 2418 $str .= $c_datatype_cell . '| ' . ('-' x ($longest_arg+4)) . ' | ' . ('-' x $longest_desc) . " |\n"; 2419 2420 while (@final_params) { 2421 my $arg = shift @final_params; 2422 my $c_datatype = shift @final_params; 2423 $c_datatype_cell = ''; 2424 if ($params_has_c_datatype) { 2425 $c_datatype = defined($c_datatype) ? wikify($wikitype, $c_datatype) : ''; 2426 $c_datatype_cell = ($longest_c_datatype > 0) ? ("| $c_datatype " . (' ' x ($longest_c_datatype - length($c_datatype)))) : ''; 2427 } 2428 my $desc = wikify($wikitype, shift @final_params); 2429 $str .= $c_datatype_cell . "| **$arg** " . (' ' x ($longest_arg - length($arg))) . "| $desc" . (' ' x ($longest_desc - length($desc))) . " |\n"; 2430 } 2431 } else { 2432 die("Unexpected wikitype!"); # should have checked this elsewhere. 2433 } 2434 $sections{'Function Parameters'} = $str; 2435 } 2436 2437 my $path = "$wikipath/$sym.${wikitype}.tmp"; 2438 open(FH, '>', $path) or die("Can't open '$path': $!\n"); 2439 2440 my $sectionsref = $wikisyms{$sym}; 2441 2442 foreach (@standard_wiki_sections) { 2443 # drop sections we either replaced or removed from the original wiki's contents. 2444 if (not defined $only_wiki_sections{$_}) { 2445 delete($$sectionsref{$_}); 2446 } 2447 } 2448 2449 my $wikisectionorderref = $wikisectionorder{$sym}; 2450 2451 # Make sure there's a footer in the wiki that puts this function in CategoryAPI... 2452 if (not $$sectionsref{'[footer]'}) { 2453 $$sectionsref{'[footer]'} = ''; 2454 push @$wikisectionorderref, '[footer]'; 2455 } 2456 2457 # If changing format, convert things that otherwise are passed through unmolested. 2458 if (defined $changeformat) { 2459 if (($dewikify_mode eq 'md') and ($origwikitype eq 'mediawiki')) { 2460 $$sectionsref{'[footer]'} =~ s/\[\[(Category[a-zA-Z0-9_]+)\]\]/[$1]($1)/g; 2461 } elsif (($dewikify_mode eq 'mediawiki') and ($origwikitype eq 'md')) { 2462 $$sectionsref{'[footer]'} =~ s/\[(Category[a-zA-Z0-9_]+)\]\(.*?\)/[[$1]]/g; 2463 } 2464 2465 foreach (keys %only_wiki_sections) { 2466 my $sect = $_; 2467 if (defined $$sectionsref{$sect}) { 2468 $$sectionsref{$sect} = wikify($wikitype, dewikify($origwikitype, $$sectionsref{$sect})); 2469 } 2470 } 2471 } 2472 2473 if ($symtype != -1) { # Don't do these in category documentation block 2474 my $footer = $$sectionsref{'[footer]'}; 2475 2476 my $symtypename; 2477 if ($symtype == 1) { 2478 $symtypename = 'Function'; 2479 } elsif ($symtype == 2) { 2480 $symtypename = 'Macro'; 2481 } elsif ($symtype == 3) { 2482 $symtypename = 'Struct'; 2483 } elsif ($symtype == 4) { 2484 $symtypename = 'Enum'; 2485 } elsif ($symtype == 5) { 2486 $symtypename = 'Datatype'; 2487 } else { 2488 die("Unexpected symbol type $symtype!"); 2489 } 2490 2491 my $symcategory = $headersymscategory{$sym}; 2492 if ($wikitype eq 'mediawiki') { 2493 $footer =~ s/\[\[CategoryAPI\]\],?\s*//g; 2494 $footer =~ s/\[\[CategoryAPI${symtypename}\]\],?\s*//g; 2495 $footer =~ s/\[\[Category${symcategory}\]\],?\s*//g if defined $symcategory; 2496 $footer = "[[CategoryAPI]], [[CategoryAPI$symtypename]]" . (defined $symcategory ? ", [[Category$symcategory]]" : '') . (($footer eq '') ? "\n" : ", $footer"); 2497 } elsif ($wikitype eq 'md') { 2498 $footer =~ s/\[CategoryAPI\]\(CategoryAPI\),?\s*//g; 2499 $footer =~ s/\[CategoryAPI${symtypename}\]\(CategoryAPI${symtypename}\),?\s*//g; 2500 $footer =~ s/\[Category${symcategory}\]\(Category${symcategory}\),?\s*//g if defined $symcategory; 2501 $footer = "[CategoryAPI](CategoryAPI), [CategoryAPI$symtypename](CategoryAPI$symtypename)" . (defined $symcategory ? ", [Category$symcategory](Category$symcategory)" : '') . (($footer eq '') ? '' : ', ') . $footer; 2502 } else { die("Unexpected wikitype '$wikitype'"); } 2503 $$sectionsref{'[footer]'} = $footer; 2504 2505 if (defined $wikipreamble) { 2506 my $wikified_preamble = wikify($wikitype, $wikipreamble); 2507 if ($wikitype eq 'mediawiki') { 2508 print FH "====== $wikified_preamble ======\n"; 2509 } elsif ($wikitype eq 'md') { 2510 print FH "###### $wikified_preamble\n"; 2511 } else { die("Unexpected wikitype '$wikitype'"); } 2512 } 2513 } 2514 2515 my $prevsectstr = ''; 2516 my @ordered_sections = (@standard_wiki_sections, defined $wikisectionorderref ? @$wikisectionorderref : ()); # this copies the arrays into one. 2517 foreach (@ordered_sections) { 2518 my $sect = $_; 2519 next if $sect eq '[start]'; 2520 next if (not defined $sections{$sect} and not defined $$sectionsref{$sect}); 2521 my $section = defined $sections{$sect} ? $sections{$sect} : $$sectionsref{$sect}; 2522 2523 if ($sect eq '[footer]') { 2524 # Make sure previous section ends with two newlines. 2525 if (substr($prevsectstr, -1) ne "\n") { 2526 print FH "\n\n"; 2527 } elsif (substr($prevsectstr, -2) ne "\n\n") { 2528 print FH "\n"; 2529 } 2530 print FH "----\n"; # It's the same in Markdown and MediaWiki. 2531 } elsif ($sect eq '[Brief]') { 2532 if ($wikitype eq 'mediawiki') { 2533 print FH "= $sym =\n\n"; 2534 } elsif ($wikitype eq 'md') { 2535 print FH "# $sym\n\n"; 2536 } else { die("Unexpected wikitype '$wikitype'"); } 2537 } else { 2538 my $sectname = $sect; 2539 if ($sectname eq 'Function Parameters') { # We use this same table for different things depending on what we're documenting, so rename it now. 2540 if (($symtype == 1) || ($symtype == 5)) { # function (or typedef, in case it's a function pointer type). 2541 } elsif ($symtype == 2) { # macro 2542 $sectname = 'Macro Parameters'; 2543 } elsif ($symtype == 3) { # struct/union 2544 $sectname = 'Fields'; 2545 } elsif ($symtype == 4) { # enum 2546 $sectname = 'Values'; 2547 } else { 2548 die("Unexpected symtype $symtype"); 2549 } 2550 } 2551 2552 if ($symtype != -1) { # Not for category documentation block 2553 if ($wikitype eq 'mediawiki') { 2554 print FH "\n== $sectname ==\n\n"; 2555 } elsif ($wikitype eq 'md') { 2556 print FH "\n## $sectname\n\n"; 2557 } else { die("Unexpected wikitype '$wikitype'"); } 2558 } 2559 } 2560 2561 my $sectstr = defined $sections{$sect} ? $sections{$sect} : $$sectionsref{$sect}; 2562 print FH $sectstr; 2563 2564 $prevsectstr = $sectstr; 2565 2566 # make sure these don't show up twice. 2567 delete($sections{$sect}); 2568 delete($$sectionsref{$sect}); 2569 } 2570 2571 print FH "\n\n"; 2572 close(FH); 2573 2574 if (defined $changeformat and ($origwikitype ne $wikitype)) { 2575 system("cd '$wikipath' ; git mv '$_.${origwikitype}' '$_.${wikitype}'"); 2576 unlink("$wikipath/$_.${origwikitype}"); 2577 } 2578 2579 rename($path, "$wikipath/$_.${wikitype}") or die("Can't rename '$path' to '$wikipath/$_.${wikitype}': $!\n"); 2580 } 2581 2582 # Write out simple redirector pages if they don't already exist. 2583 foreach (keys %referenceonly) { 2584 my $sym = $_; 2585 my $refersto = $referenceonly{$sym}; 2586 my $path = "$wikipath/$sym.md"; # we only do Markdown for these. 2587 next if (-f $path); # don't overwrite if it already exists. Delete the file if you need a rebuild! 2588 open(FH, '>', $path) or die("Can't open '$path': $!\n"); 2589 2590 if (defined $wikipreamble) { 2591 my $wikified_preamble = wikify('md', $wikipreamble); 2592 print FH "###### $wikified_preamble\n"; 2593 } 2594 2595 my $category = 'CategoryAPIMacro'; 2596 if ($headersymstype{$refersto} == 4) { 2597 $category = 'CategoryAPIEnumerators'; # NOT CategoryAPIEnum! 2598 } 2599 2600 print FH "# $sym\n\nPlease refer to [$refersto]($refersto) for details.\n\n"; 2601 print FH "----\n"; 2602 print FH "[CategoryAPI](CategoryAPI), [$category]($category)\n\n"; 2603 2604 close(FH); 2605 } 2606 2607 # Write out Category pages... 2608 foreach (keys %headercategorydocs) { 2609 my $cat = $_; 2610 my $sym = $headercategorydocs{$cat}; # fake symbol 2611 my $raw = $headersyms{$sym}; # raw doxygen text with comment characters stripped from start/end and start of each line. 2612 my $wikitype = defined($wikitypes{$sym}) ? $wikitypes{$sym} : 'md'; 2613 my $path = "$wikipath/Category$cat.$wikitype"; 2614 2615 $raw = wordwrap(wikify($wikitype, $raw)); 2616 2617 my $tmppath = "$path.tmp"; 2618 open(FH, '>', $tmppath) or die("Can't open '$tmppath': $!\n"); 2619 print FH "$raw\n\n"; 2620 2621 if (! -f $path) { # Doesn't exist at all? Write out a template file. 2622 # If writing from scratch, it's always a Markdown file. 2623 die("Unexpected wikitype '$wikitype'!") if $wikitype ne 'md'; 2624 print FH <<__EOF__ 2625 2626<!-- END CATEGORY DOCUMENTATION --> 2627 2628## Functions 2629 2630<!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. --> 2631<!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIFunction --> 2632<!-- END CATEGORY LIST --> 2633 2634## Datatypes 2635 2636<!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. --> 2637<!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIDatatype --> 2638<!-- END CATEGORY LIST --> 2639 2640## Structs 2641 2642<!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. --> 2643<!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIStruct --> 2644<!-- END CATEGORY LIST --> 2645 2646## Enums 2647 2648<!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. --> 2649<!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIEnum --> 2650<!-- END CATEGORY LIST --> 2651 2652## Macros 2653 2654<!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. --> 2655<!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIMacro --> 2656<!-- END CATEGORY LIST --> 2657 2658---- 2659[CategoryAPICategory](CategoryAPICategory) 2660 2661__EOF__ 2662; 2663 } else { 2664 my $endstr = $wikisyms{$sym}->{'[footer]'}; 2665 if (defined($endstr)) { 2666 print FH $endstr; 2667 } 2668 } 2669 2670 close(FH); 2671 rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n"); 2672 } 2673 2674 # Write out READMEs... 2675 if (defined $readmepath) { 2676 if ( -d $readmepath ) { 2677 mkdir($wikireadmepath); # just in case 2678 opendir(DH, $readmepath) or die("Can't opendir '$readmepath': $!\n"); 2679 while (my $d = readdir(DH)) { 2680 my $dent = $d; 2681 if ($dent =~ /\AREADME\-(.*?\.md)\Z/) { # we only bridge Markdown files here. 2682 my $wikifname = $1; 2683 next if $wikifname eq 'FrontPage.md'; 2684 filecopy("$readmepath/$dent", "$wikireadmepath/$wikifname", "\n"); 2685 } 2686 } 2687 closedir(DH); 2688 2689 my @pages = (); 2690 opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n"); 2691 while (my $d = readdir(DH)) { 2692 my $dent = $d; 2693 if ($dent =~ /\A(.*?)\.(mediawiki|md)\Z/) { 2694 my $wikiname = $1; 2695 next if $wikiname eq 'FrontPage'; 2696 push @pages, $wikiname; 2697 } 2698 } 2699 closedir(DH); 2700 2701 open(FH, '>', "$wikireadmepath/FrontPage.md") or die("Can't open '$wikireadmepath/FrontPage.md': $!\n"); 2702 print FH "# All READMEs available here\n\n"; 2703 foreach (sort @pages) { 2704 my $wikiname = $_; 2705 print FH "- [$wikiname]($wikiname)\n"; 2706 } 2707 close(FH); 2708 } 2709 } 2710 2711 # Write out quick reference pages... 2712 if ($quickrefenabled) { 2713 generate_quickref(\%briefs, "$wikipath/QuickReference.md", 0); 2714 generate_quickref(\%briefs, "$wikipath/QuickReferenceNoUnicode.md", 1); 2715 } 2716} elsif ($copy_direction == -2) { # --copy-to-manpages 2717 # This only takes from the wiki data, since it has sections we omit from the headers, like code examples. 2718 2719 File::Path::make_path("$manpath/man3"); 2720 2721 $dewikify_mode = 'manpage'; 2722 $wordwrap_mode = 'manpage'; 2723 2724 my $introtxt = ''; 2725 if (0) { 2726 open(FH, '<', "$srcpath/LICENSE.txt") or die("Can't open '$srcpath/LICENSE.txt': $!\n"); 2727 while (<FH>) { 2728 chomp; 2729 $introtxt .= ".\\\" $_\n"; 2730 } 2731 close(FH); 2732 } 2733 2734 if (!$gitrev) { 2735 $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`; 2736 chomp($gitrev); 2737 } 2738 2739 # !!! FIXME 2740 open(FH, '<', "$srcpath/$versionfname") or die("Can't open '$srcpath/$versionfname': $!\n"); 2741 my $majorver = 0; 2742 my $minorver = 0; 2743 my $microver = 0; 2744 while (<FH>) { 2745 chomp; 2746 if (/$versionmajorregex/) { 2747 $majorver = int($1); 2748 } elsif (/$versionminorregex/) { 2749 $minorver = int($1); 2750 } elsif (/$versionmicroregex/) { 2751 $microver = int($1); 2752 } 2753 } 2754 close(FH); 2755 my $fullversion = "$majorver.$minorver.$microver"; 2756 2757 foreach (keys %headersyms) { 2758 my $sym = $_; 2759 next if not defined $wikisyms{$sym}; # don't have a page for that function, skip it. 2760 next if $sym =~ /\A\[category documentation\]/; # not real symbols 2761 my $symtype = $headersymstype{$sym}; 2762 my $wikitype = $wikitypes{$sym}; 2763 my $sectionsref = $wikisyms{$sym}; 2764 my $remarks = $sectionsref->{'Remarks'}; 2765 my $params = $sectionsref->{'Function Parameters'}; 2766 my $returns = $sectionsref->{'Return Value'}; 2767 my $version = $sectionsref->{'Version'}; 2768 my $threadsafety = $sectionsref->{'Thread Safety'}; 2769 my $related = $sectionsref->{'See Also'}; 2770 my $examples = $sectionsref->{'Code Examples'}; 2771 my $deprecated = $sectionsref->{'Deprecated'}; 2772 my $headerfile = $manpageheaderfiletext; 2773 $headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g; 2774 $headerfile .= "\n"; 2775 2776 my $mansection; 2777 my $mansectionname; 2778 if (($symtype == 1) || ($symtype == 2)) { # functions or macros 2779 $mansection = '3'; 2780 $mansectionname = 'FUNCTIONS'; 2781 } elsif (($symtype >= 3) && ($symtype <= 5)) { # struct/union/enum/typedef 2782 $mansection = '3type'; 2783 $mansectionname = 'DATATYPES'; 2784 } else { 2785 die("Unexpected symtype $symtype"); 2786 } 2787 2788 my $brief = $sectionsref->{'[Brief]'}; 2789 my $decl = $headerdecls{$sym}; 2790 my $str = ''; 2791 2792 $brief = "$brief"; 2793 $brief =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms; 2794 $brief =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms; 2795 $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary. 2796 my @briefsplit = split /\n/, $brief; 2797 $brief = shift @briefsplit; 2798 $brief = dewikify($wikitype, $brief); 2799 2800 if (defined $remarks) { 2801 $remarks = dewikify($wikitype, join("\n", @briefsplit) . $remarks); 2802 } 2803 2804 $str .= $introtxt; 2805 2806 $str .= ".\\\" This manpage content is licensed under Creative Commons\n"; 2807 $str .= ".\\\" Attribution 4.0 International (CC BY 4.0)\n"; 2808 $str .= ".\\\" https://creativecommons.org/licenses/by/4.0/\n"; 2809 $str .= ".\\\" This manpage was generated from ${projectshortname}'s wiki page for $sym:\n"; 2810 $str .= ".\\\" $wikiurl/$sym\n"; 2811 $str .= ".\\\" Generated with SDL/build-scripts/wikiheaders.pl\n"; 2812 $str .= ".\\\" revision $gitrev\n" if $gitrev ne ''; 2813 $str .= ".\\\" Please report issues in this manpage's content at:\n"; 2814 $str .= ".\\\" $bugreporturl\n"; 2815 $str .= ".\\\" Please report issues in the generation of this manpage from the wiki at:\n"; 2816 $str .= ".\\\" https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n"; 2817 $str .= ".\\\" $projectshortname can be found at $projecturl\n"; 2818 2819 # 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. 2820 # This wizadry is from https://web.archive.org/web/20060102165607/http://people.debian.org/~branden/talks/wtfm/wtfm.pdf 2821 $str .= ".de URL\n"; 2822 $str .= '\\$2 \(laURL: \\$1 \(ra\\$3' . "\n"; 2823 $str .= "..\n"; 2824 $str .= '.if \n[.g] .mso www.tmac' . "\n"; 2825 2826 $str .= ".TH $sym $mansection \"$projectshortname $fullversion\" \"$projectfullname\" \"$projectshortname$majorver $mansectionname\"\n"; 2827 $str .= ".SH NAME\n"; 2828 2829 $str .= "$sym"; 2830 $str .= " \\- $brief" if (defined $brief); 2831 $str .= "\n"; 2832 2833 if (defined $deprecated) { 2834 $str .= ".SH DEPRECATED\n"; 2835 $str .= dewikify($wikitype, $deprecated) . "\n"; 2836 } 2837 2838 if (defined $headerfile) { 2839 $str .= ".SH HEADER FILE\n"; 2840 $str .= dewikify($wikitype, $headerfile) . "\n"; 2841 } 2842 2843 $str .= ".SH SYNOPSIS\n"; 2844 $str .= ".nf\n"; 2845 $str .= ".B #include \\(dq$mainincludefname\\(dq\n"; 2846 $str .= ".PP\n"; 2847 2848 my @decllines = split /\n/, $decl; 2849 foreach (@decllines) { 2850 $str .= ".BI \"$_\n"; 2851 } 2852 $str .= ".fi\n"; 2853 2854 if (defined $remarks) { 2855 $str .= ".SH DESCRIPTION\n"; 2856 $str .= $remarks . "\n"; 2857 } 2858 2859 if (defined $params) { 2860 if (($symtype == 1) || ($symtype == 5)) { 2861 $str .= ".SH FUNCTION PARAMETERS\n"; 2862 } elsif ($symtype == 2) { # macro 2863 $str .= ".SH MACRO PARAMETERS\n"; 2864 } elsif ($symtype == 3) { # struct/union 2865 $str .= ".SH FIELDS\n"; 2866 } elsif ($symtype == 4) { # enum 2867 $str .= ".SH VALUES\n"; 2868 } else { 2869 die("Unexpected symtype $symtype"); 2870 } 2871 2872 my @lines = split /\n/, $params; 2873 if ($wikitype eq 'mediawiki') { 2874 die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start 2875 while (scalar(@lines) >= 3) { 2876 my $c_datatype = shift @lines; 2877 my $name = shift @lines; 2878 my $desc = shift @lines; 2879 my $terminator; # the '|-' or '|}' line. 2880 2881 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. 2882 $terminator = $desc; 2883 $desc = $name; 2884 $name = $c_datatype; 2885 $c_datatype = ''; 2886 } else { 2887 $terminator = shift @lines; 2888 } 2889 2890 last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table. 2891 $name =~ s/\A\|\s*//; 2892 $name =~ s/\A\*\*(.*?)\*\*/$1/; 2893 $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/; 2894 $desc =~ s/\A\|\s*//; 2895 $desc = dewikify($wikitype, $desc); 2896 #print STDERR "SYM: $sym CDATATYPE: $c_datatype NAME: $name DESC: $desc TERM: $terminator\n"; 2897 2898 $str .= ".TP\n"; 2899 $str .= ".I $name\n"; 2900 $str .= "$desc\n"; 2901 } 2902 } elsif ($wikitype eq 'md') { 2903 my $l; 2904 $l = shift @lines; 2905 die("Unexpected data parsing Markdown table") if (not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/); 2906 $l = shift @lines; 2907 die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/); 2908 while (scalar(@lines) >= 1) { 2909 $l = shift @lines; 2910 my $name; 2911 my $desc; 2912 if ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) { 2913 # c datatype is $1, but we don't care about it here. 2914 $name = $2; 2915 $desc = $3; 2916 } elsif ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) { 2917 $name = $1; 2918 $desc = $2; 2919 } else { 2920 last; # we seem to have run out of table. 2921 } 2922 2923 $name =~ s/\A\*\*(.*?)\*\*/$1/; 2924 $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/; 2925 $desc = dewikify($wikitype, $desc); 2926 2927 $str .= ".TP\n"; 2928 $str .= ".I $name\n"; 2929 $str .= "$desc\n"; 2930 } 2931 } else { 2932 die("write me"); 2933 } 2934 } 2935 2936 if (defined $returns) { 2937 $returns = dewikify($wikitype, $returns); 2938 $returns =~ s/\A\(.*?\)\s*//; # Chop datatype in parentheses off the front. 2939 $str .= ".SH RETURN VALUE\n"; 2940 $str .= "$returns\n"; 2941 } 2942 2943 if (defined $examples) { 2944 $str .= ".SH CODE EXAMPLES\n"; 2945 $dewikify_manpage_code_indent = 0; 2946 $str .= dewikify($wikitype, $examples) . "\n"; 2947 $dewikify_manpage_code_indent = 1; 2948 } 2949 2950 if (defined $threadsafety) { 2951 $str .= ".SH THREAD SAFETY\n"; 2952 $str .= dewikify($wikitype, $threadsafety) . "\n"; 2953 } 2954 2955 if (defined $version) { 2956 $str .= ".SH AVAILABILITY\n"; 2957 $str .= dewikify($wikitype, $version) . "\n"; 2958 } 2959 2960 if (defined $related) { 2961 $str .= ".SH SEE ALSO\n"; 2962 # !!! FIXME: lots of code duplication in all of these. 2963 my $v = dewikify($wikitype, $related); 2964 my @desclines = split /\n/, $v; 2965 my $nextstr = ''; 2966 foreach (@desclines) { 2967 s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func" 2968 s/\[\[(.*?)\]\]/$1/; # in case some wikilinks remain. 2969 s/\[(.*?)\]\(.*?\)/$1/; # in case some wikilinks remain. 2970 s/\A\*\s*\Z//; 2971 s/\A\/*//; 2972 s/\A\.BR\s+//; # dewikify added this, but we want to handle it. 2973 s/\A\.I\s+//; # dewikify added this, but we want to handle it. 2974 s/\A\s*[\:\*\-]\s*//; 2975 s/\A\s+//; 2976 s/\s+\Z//; 2977 next if $_ eq ''; 2978 my $seealso_symtype = $headersymstype{$_}; 2979 my $seealso_mansection = '3'; 2980 if (defined($seealso_symtype) && ($seealso_symtype >= 3) && ($seealso_symtype <= 5)) { # struct/union/enum/typedef 2981 $seealso_mansection = '3type'; 2982 } 2983 $str .= "$nextstr.BR $_ ($seealso_mansection)"; 2984 $nextstr = ",\n"; 2985 } 2986 $str .= "\n"; 2987 } 2988 2989 if (0) { 2990 $str .= ".SH COPYRIGHT\n"; 2991 $str .= "This manpage is licensed under\n"; 2992 $str .= ".UR https://creativecommons.org/licenses/by/4.0/\n"; 2993 $str .= "Creative Commons Attribution 4.0 International (CC BY 4.0)\n"; 2994 $str .= ".UE\n"; 2995 $str .= ".PP\n"; 2996 $str .= "This manpage was generated from\n"; 2997 $str .= ".UR $wikiurl/$sym\n"; 2998 $str .= "${projectshortname}'s wiki\n"; 2999 $str .= ".UE\n"; 3000 $str .= "using SDL/build-scripts/wikiheaders.pl"; 3001 $str .= " revision $gitrev" if $gitrev ne ''; 3002 $str .= ".\n"; 3003 $str .= "Please report issues in this manpage at\n"; 3004 $str .= ".UR $bugreporturl\n"; 3005 $str .= "our bugtracker!\n"; 3006 $str .= ".UE\n"; 3007 } 3008 3009 my $path = "$manpath/man3/$_.$mansection"; 3010 my $tmppath = "$path.tmp"; 3011 open(FH, '>', $tmppath) or die("Can't open '$tmppath': $!\n"); 3012 print FH $str; 3013 close(FH); 3014 rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n"); 3015 } 3016 3017} elsif ($copy_direction == -4) { # --copy-to-latex 3018 # This only takes from the wiki data, since it has sections we omit from the headers, like code examples. 3019 3020 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"; 3021 3022 $dewikify_mode = 'LaTeX'; 3023 $wordwrap_mode = 'LaTeX'; 3024 3025 # !!! FIXME: code duplication with --copy-to-manpages section. 3026 3027 my $introtxt = ''; 3028 if (0) { 3029 open(FH, '<', "$srcpath/LICENSE.txt") or die("Can't open '$srcpath/LICENSE.txt': $!\n"); 3030 while (<FH>) { 3031 chomp; 3032 $introtxt .= ".\\\" $_\n"; 3033 } 3034 close(FH); 3035 } 3036 3037 if (!$gitrev) { 3038 $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`; 3039 chomp($gitrev); 3040 } 3041 3042 # !!! FIXME 3043 open(FH, '<', "$srcpath/$versionfname") or die("Can't open '$srcpath/$versionfname': $!\n"); 3044 my $majorver = 0; 3045 my $minorver = 0; 3046 my $microver = 0; 3047 while (<FH>) { 3048 chomp; 3049 if (/$versionmajorregex/) { 3050 $majorver = int($1); 3051 } elsif (/$versionminorregex/) { 3052 $minorver = int($1); 3053 } elsif (/$versionmicroregex/) { 3054 $microver = int($1); 3055 } 3056 } 3057 close(FH); 3058 my $fullversion = "$majorver.$minorver.$microver"; 3059 3060 my $latex_fname = "$srcpath/$projectshortname.tex"; 3061 my $latex_tmpfname = "$latex_fname.tmp"; 3062 open(TEXFH, '>', "$latex_tmpfname") or die("Can't open '$latex_tmpfname' for writing: $!\n"); 3063 3064 print TEXFH <<__EOF__ 3065\\documentclass{book} 3066 3067\\usepackage{listings} 3068\\usepackage{color} 3069\\usepackage{hyperref} 3070 3071\\definecolor{dkgreen}{rgb}{0,0.6,0} 3072\\definecolor{gray}{rgb}{0.5,0.5,0.5} 3073\\definecolor{mauve}{rgb}{0.58,0,0.82} 3074 3075\\setcounter{secnumdepth}{0} 3076 3077\\lstset{frame=tb, 3078 language=C, 3079 aboveskip=3mm, 3080 belowskip=3mm, 3081 showstringspaces=false, 3082 columns=flexible, 3083 basicstyle={\\small\\ttfamily}, 3084 numbers=none, 3085 numberstyle=\\tiny\\color{gray}, 3086 keywordstyle=\\color{blue}, 3087 commentstyle=\\color{dkgreen}, 3088 stringstyle=\\color{mauve}, 3089 breaklines=true, 3090 breakatwhitespace=true, 3091 tabsize=3 3092} 3093 3094\\begin{document} 3095\\frontmatter 3096 3097\\title{$projectfullname $majorver.$minorver.$microver Reference Manual} 3098\\author{The $projectshortname Developers} 3099\\maketitle 3100 3101\\mainmatter 3102 3103__EOF__ 3104; 3105 3106 # !!! FIXME: Maybe put this in the book intro? print TEXFH $introtxt; 3107 3108 # Sort symbols by symbol type, then alphabetically. 3109 my @headersymskeys = sort { 3110 my $symtypea = $headersymstype{$a}; 3111 my $symtypeb = $headersymstype{$b}; 3112 $symtypea = 3 if ($symtypea > 3); 3113 $symtypeb = 3 if ($symtypeb > 3); 3114 my $rc = $symtypea <=> $symtypeb; 3115 if ($rc == 0) { 3116 $rc = lc($a) cmp lc($b); 3117 } 3118 return $rc; 3119 } keys %headersyms; 3120 3121 my $current_symtype = 0; 3122 my $current_chapter = ''; 3123 3124 foreach (@headersymskeys) { 3125 my $sym = $_; 3126 next if not defined $wikisyms{$sym}; # don't have a page for that function, skip it. 3127 next if $sym =~ /\A\[category documentation\]/; # not real symbols. 3128 my $symtype = $headersymstype{$sym}; 3129 my $wikitype = $wikitypes{$sym}; 3130 my $sectionsref = $wikisyms{$sym}; 3131 my $remarks = $sectionsref->{'Remarks'}; 3132 my $params = $sectionsref->{'Function Parameters'}; 3133 my $returns = $sectionsref->{'Return Value'}; 3134 my $version = $sectionsref->{'Version'}; 3135 my $threadsafety = $sectionsref->{'Thread Safety'}; 3136 my $related = $sectionsref->{'See Also'}; 3137 my $examples = $sectionsref->{'Code Examples'}; 3138 my $deprecated = $sectionsref->{'Deprecated'}; 3139 my $headerfile = $manpageheaderfiletext; 3140 $headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g; 3141 $headerfile .= "\n"; 3142 3143 my $brief = $sectionsref->{'[Brief]'}; 3144 my $decl = $headerdecls{$sym}; 3145 my $str = ''; 3146 3147 if ($current_symtype != $symtype) { 3148 my $newchapter = ''; 3149 if ($symtype == 1) { 3150 $newchapter = 'Functions'; 3151 } elsif ($symtype == 2) { 3152 $newchapter = 'Macros'; 3153 } else { 3154 $newchapter = 'Datatypes'; 3155 } 3156 3157 if ($current_chapter ne $newchapter) { 3158 $str .= "\n\n\\chapter{$projectshortname $newchapter}\n\n\\clearpage\n\n"; 3159 $current_chapter = $newchapter; 3160 } 3161 $current_symtype = $symtype; 3162 } 3163 3164 $brief = "$brief"; 3165 $brief =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms; 3166 $brief =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms; 3167 $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary. 3168 my @briefsplit = split /\n/, $brief; 3169 $brief = shift @briefsplit; 3170 $brief = dewikify($wikitype, $brief); 3171 3172 if (defined $remarks) { 3173 $remarks = dewikify($wikitype, join("\n", @briefsplit) . $remarks); 3174 } 3175 3176 my $escapedsym = escLaTeX($sym); 3177 $str .= "\\hypertarget{$sym}{%\n\\section{$escapedsym}\\label{$sym}}\n\n"; 3178 $str .= $brief if (defined $brief); 3179 $str .= "\n\n"; 3180 3181 if (defined $deprecated) { 3182 $str .= "\\subsection{Deprecated}\n\n"; 3183 $str .= dewikify($wikitype, $deprecated) . "\n"; 3184 } 3185 3186 if (defined $headerfile) { 3187 $str .= "\\subsection{Header File}\n\n"; 3188 $str .= dewikify($wikitype, $headerfile) . "\n"; 3189 } 3190 3191 $str .= "\\subsection{Syntax}\n\n"; 3192 $str .= "\\begin{lstlisting}\n$decl\n\\end{lstlisting}\n"; 3193 3194 if (defined $params) { 3195 if (($symtype == 1) || ($symtype == 5)) { 3196 $str .= "\\subsection{Function Parameters}\n\n"; 3197 } elsif ($symtype == 2) { # macro 3198 $str .= "\\subsection{Macro Parameters}\n\n"; 3199 } elsif ($symtype == 3) { # struct/union 3200 $str .= "\\subsection{Fields}\n\n"; 3201 } elsif ($symtype == 4) { # enum 3202 $str .= "\\subsection{Values}\n\n"; 3203 } else { 3204 die("Unexpected symtype $symtype"); 3205 } 3206 3207 $str .= "\\begin{center}\n"; 3208 $str .= " \\begin{tabular}{ | l | p{0.75\\textwidth} |}\n"; 3209 $str .= " \\hline\n"; 3210 3211 # !!! FIXME: this table parsing has gotten complicated and is pasted three times in this file; move it to a subroutine! 3212 my @lines = split /\n/, $params; 3213 if ($wikitype eq 'mediawiki') { 3214 die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start 3215 while (scalar(@lines) >= 3) { 3216 my $name = shift @lines; 3217 my $desc = shift @lines; 3218 my $terminator = shift @lines; # the '|-' or '|}' line. 3219 last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table. 3220 $name =~ s/\A\|\s*//; 3221 $name =~ s/\A\*\*(.*?)\*\*/$1/; 3222 $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/; 3223 $name = escLaTeX($name); 3224 $desc =~ s/\A\|\s*//; 3225 $desc = dewikify($wikitype, $desc); 3226 #print STDERR "FN: $sym NAME: $name DESC: $desc TERM: $terminator\n"; 3227 $str .= " \\textbf{$name} & $desc \\\\ \\hline\n"; 3228 } 3229 } elsif ($wikitype eq 'md') { 3230 my $l; 3231 $l = shift @lines; 3232 die("Unexpected data parsing Markdown table") if (not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/); 3233 $l = shift @lines; 3234 die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/); 3235 while (scalar(@lines) >= 1) { 3236 $l = shift @lines; 3237 my $name; 3238 my $desc; 3239 if ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) { 3240 # c datatype is $1, but we don't care about it here. 3241 $name = $2; 3242 $desc = $3; 3243 } elsif ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) { 3244 $name = $1; 3245 $desc = $2; 3246 } else { 3247 last; # we seem to have run out of table. 3248 } 3249 3250 $name =~ s/\A\*\*(.*?)\*\*/$1/; 3251 $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/; 3252 $name = escLaTeX($name); 3253 $desc = dewikify($wikitype, $desc); 3254 $str .= " \\textbf{$name} & $desc \\\\ \\hline\n"; 3255 } 3256 } else { 3257 die("write me"); 3258 } 3259 3260 $str .= " \\end{tabular}\n"; 3261 $str .= "\\end{center}\n"; 3262 } 3263 3264 if (defined $returns) { 3265 $returns = dewikify($wikitype, $returns); 3266 $returns =~ s/\A\(.*?\)\s*//; # Chop datatype in parentheses off the front. 3267 $str .= "\\subsection{Return Value}\n\n"; 3268 $str .= "$returns\n"; 3269 } 3270 3271 if (defined $remarks) { 3272 $str .= "\\subsection{Remarks}\n\n"; 3273 $str .= $remarks . "\n"; 3274 } 3275 3276 if (defined $examples) { 3277 $str .= "\\subsection{Code Examples}\n\n"; 3278 $dewikify_manpage_code_indent = 0; 3279 $str .= dewikify($wikitype, $examples) . "\n"; 3280 $dewikify_manpage_code_indent = 1; 3281 } 3282 3283 if (defined $threadsafety) { 3284 $str .= "\\subsection{Thread Safety}\n\n"; 3285 $str .= dewikify($wikitype, $threadsafety) . "\n"; 3286 } 3287 3288 if (defined $version) { 3289 $str .= "\\subsection{Version}\n\n"; 3290 $str .= dewikify($wikitype, $version) . "\n"; 3291 } 3292 3293 if (defined $related) { 3294 $str .= "\\subsection{See Also}\n\n"; 3295 $str .= "\\begin{itemize}\n"; 3296 # !!! FIXME: lots of code duplication in all of these. 3297 my $v = dewikify($wikitype, $related); 3298 my @desclines = split /\n/, $v; 3299 my $nextstr = ''; 3300 foreach (@desclines) { 3301 s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func" 3302 s/\[\[(.*?)\]\]/$1/; # in case some wikilinks remain. 3303 s/\[(.*?)\]\(.*?\)/$1/; # in case some wikilinks remain. 3304 s/\A\*\s*\Z//; 3305 s/\A\s*\\item\s*//; 3306 s/\A\/*//; 3307 s/\A\s*[\:\*\-]\s*//; 3308 s/\A\s+//; 3309 s/\s+\Z//; 3310 next if $_ eq ''; 3311 next if $_ eq '\begin{itemize}'; 3312 next if $_ eq '\end{itemize}'; 3313 $str .= " \\item $_\n"; 3314 } 3315 $str .= "\\end{itemize}\n"; 3316 $str .= "\n"; 3317 } 3318 3319 # !!! FIXME: Maybe put copyright in the book intro? 3320 if (0) { 3321 $str .= ".SH COPYRIGHT\n"; 3322 $str .= "This manpage is licensed under\n"; 3323 $str .= ".UR https://creativecommons.org/licenses/by/4.0/\n"; 3324 $str .= "Creative Commons Attribution 4.0 International (CC BY 4.0)\n"; 3325 $str .= ".UE\n"; 3326 $str .= ".PP\n"; 3327 $str .= "This manpage was generated from\n"; 3328 $str .= ".UR $wikiurl/$sym\n"; 3329 $str .= "${projectshortname}'s wiki\n"; 3330 $str .= ".UE\n"; 3331 $str .= "using SDL/build-scripts/wikiheaders.pl"; 3332 $str .= " revision $gitrev" if $gitrev ne ''; 3333 $str .= ".\n"; 3334 $str .= "Please report issues in this manpage at\n"; 3335 $str .= ".UR $bugreporturl\n"; 3336 $str .= "our bugtracker!\n"; 3337 $str .= ".UE\n"; 3338 } 3339 3340 $str .= "\\clearpage\n\n"; 3341 3342 print TEXFH $str; 3343 } 3344 3345 print TEXFH "\\end{document}\n\n"; 3346 close(TEXFH); 3347 rename($latex_tmpfname, $latex_fname) or die("Can't rename '$latex_tmpfname' to '$latex_fname': $!\n"); 3348 3349} elsif ($copy_direction == -3) { # --report-coverage-gaps 3350 foreach (@coverage_gap) { 3351 print("$_\n"); 3352 } 3353} 3354 3355# end of wikiheaders.pl ... 3356