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