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