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