wikiheaders.pl 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. #!/usr/bin/perl -w
  2. use warnings;
  3. use strict;
  4. use Text::Wrap;
  5. my $srcpath = undef;
  6. my $wikipath = undef;
  7. my $warn_about_missing = 0;
  8. my $copy_direction = 0;
  9. foreach (@ARGV) {
  10. $warn_about_missing = 1, next if $_ eq '--warn-about-missing';
  11. $copy_direction = 1, next if $_ eq '--copy-to-headers';
  12. $copy_direction = -1, next if $_ eq '--copy-to-wiki';
  13. $srcpath = $_, next if not defined $srcpath;
  14. $wikipath = $_, next if not defined $wikipath;
  15. }
  16. my $wordwrap_default_columns = 76;
  17. sub wordwrap {
  18. my $str = shift;
  19. my $columns = shift;
  20. $columns = $wordwrap_default_columns if not defined $columns;
  21. $columns += $wordwrap_default_columns if $columns < 0;
  22. $Text::Wrap::columns = $columns;
  23. my $retval = '';
  24. # !!! FIXME: at some point it would be neat if this understood lists, so
  25. #
  26. # * Item 1
  27. # * Item 2
  28. #
  29. # was understood to a) not wrap into one line and b) knew to wrap so overflow
  30. # lined up _indented_ under the '*' (etc) char. But we don't do that currently,
  31. # so cheat and make each bullet its own paragraph instead, which is Good Enough
  32. # for the time being.
  33. while ($str =~ s/(.*?)(\n*\`\`\`.*?\`\`\`\n*|\n*\<syntaxhighlight.*?\<\/syntaxhighlight\>\n*)//ms) {
  34. $retval .= fill('', '', $1); # wrap it.
  35. $retval .= $2; # don't wrap it.
  36. }
  37. return $retval . fill('', '', $str); # wrap what's left.
  38. }
  39. sub wikify {
  40. my $wikitype = shift;
  41. my $str = shift;
  42. if ($wikitype eq 'mediawiki') {
  43. # Convert obvious SDL things to wikilinks.
  44. $str =~ s/\b(SDL_[a-zA-Z0-9_]+)/[[$1]]/gms;
  45. # Make some Markdown things into MediaWiki...
  46. $str =~ s/\`\`\`(c|c++)(.*?)\`\`\`/<syntaxhighlight lang='$1'>$2<\/syntaxhighlight>/gms;
  47. # <code></code> is also popular. :/
  48. $str =~ s/\`(.*?)\`/<code>$1<\/code>/gms;
  49. # bold+italic
  50. $str =~ s/\\*\*\*(.*?)\*\*\*/'''''$1'''''/gms;
  51. # bold
  52. $str =~ s/\*\*(.*?)\*\*/'''$1'''/gms;
  53. # italic
  54. $str =~ s/\*(.*?)\*/''$1''/gms;
  55. } elsif ($wikitype eq 'md') {
  56. # Convert obvious SDL things to wikilinks.
  57. $str =~ s/\b(SDL_[a-zA-Z0-9_]+)/[$1]($1)/gms;
  58. }
  59. return $str;
  60. }
  61. sub dewikify {
  62. my $wikitype = shift;
  63. my $str = shift;
  64. return '' if not defined $str;
  65. my @lines = split /\n/, $str;
  66. return '' if scalar(@lines) == 0;
  67. my $iwikitype = 0;
  68. if ($wikitype eq 'mediawiki') {
  69. $iwikitype = 1;
  70. } elsif ($wikitype eq 'md') {
  71. $iwikitype = 2;
  72. } else {
  73. die("Unexpected wikitype '$wikitype'\n");
  74. }
  75. while (1) {
  76. my $l = shift @lines;
  77. last if not defined $l;
  78. chomp($l);
  79. $l =~ s/\A\s*//;
  80. $l =~ s/\s*\Z//;
  81. next if ($l eq '');
  82. next if ($iwikitype == 1) and ($l =~ /\A\= .*? \=\Z/);
  83. next if ($iwikitype == 1) and ($l =~ /\A\=\= .*? \=\=\Z/);
  84. next if ($iwikitype == 2) and ($l =~ /\A\#\# /);
  85. unshift @lines, $l;
  86. last;
  87. }
  88. while (1) {
  89. my $l = pop @lines;
  90. last if not defined $l;
  91. chomp($l);
  92. $l =~ s/\A\s*//;
  93. $l =~ s/\s*\Z//;
  94. next if ($l eq '');
  95. push @lines, $l;
  96. last;
  97. }
  98. $str = '';
  99. foreach (@lines) {
  100. chomp;
  101. s/\A\s*//;
  102. s/\s*\Z//;
  103. $str .= "$_\n";
  104. }
  105. if ($iwikitype == 1) { #($wikitype eq 'mediawiki')
  106. # Doxygen supports Markdown (and it just simply looks better than MediaWiki
  107. # when looking at the raw headers, so do some conversions here as necessary.
  108. $str =~ s/\[\[(SDL_[a-zA-Z0-9_]+)\]\]/$1/gms; # Dump obvious wikilinks.
  109. # convert mediawiki syntax highlighting to Markdown backticks.
  110. $str =~ s/\<syntaxhighlight lang='?(.*?)'?>(.*?)<\/syntaxhighlight>/```$1$2```/gms;
  111. # <code></code> is also popular. :/
  112. $str =~ s/\<code>(.*?)<\/code>/`$1`/gms;
  113. # bold+italic
  114. $str =~ s/\'''''(.*?)'''''/***$1***/gms;
  115. # bold
  116. $str =~ s/\'''(.*?)'''/**$1**/gms;
  117. # italic
  118. $str =~ s/\''(.*?)''/*$1*/gms;
  119. }
  120. return $str;
  121. }
  122. sub usage {
  123. die("USAGE: $0 <source code git clone path> <wiki git clone path> [--copy-to-headers|--copy-to-wiki] [--warn-about-missing]\n\n");
  124. }
  125. usage() if not defined $srcpath;
  126. usage() if not defined $wikipath;
  127. #usage() if $copy_direction == 0;
  128. my @standard_wiki_sections = (
  129. 'Draft',
  130. '[Brief]',
  131. 'Syntax',
  132. 'Remarks',
  133. 'Function Parameters',
  134. 'Return Value',
  135. 'Version',
  136. 'Related Functions'
  137. );
  138. my %headers = (); # $headers{"SDL_audio.h"} -> reference to an array of all lines of text in SDL_audio.h.
  139. my %headerfuncs = (); # $headerfuncs{"SDL_OpenAudio"} -> string of header documentation for SDL_OpenAudio, with comment '*' bits stripped from the start. Newlines embedded!
  140. my %headerdecls = ();
  141. my %headerfuncslocation = (); # $headerfuncslocation{"SDL_OpenAudio"} -> name of header holding SDL_OpenAudio define ("SDL_audio.h" in this case).
  142. my %headerfuncschunk = (); # $headerfuncschunk{"SDL_OpenAudio"} -> offset in array in %headers that should be replaced for this function.
  143. my $incpath = "$srcpath/include";
  144. opendir(DH, $incpath) or die("Can't opendir '$incpath': $!\n");
  145. while (readdir(DH)) {
  146. my $dent = $_;
  147. next if not $dent =~ /\ASDL.*?\.h\Z/; # just SDL*.h headers.
  148. open(FH, '<', "$incpath/$dent") or die("Can't open '$incpath/$dent': $!\n");
  149. my @contents = ();
  150. while (<FH>) {
  151. chomp;
  152. if (not /\A\/\*\*/) { # not doxygen comment start?
  153. push @contents, $_;
  154. next;
  155. }
  156. my @templines = ();
  157. push @templines, $_;
  158. my $str = '';
  159. while (<FH>) {
  160. chomp;
  161. push @templines, $_;
  162. last if /\A\s*\*\/\Z/;
  163. s/\A\s*\*\s*//;
  164. $str .= "$_\n";
  165. }
  166. my $decl = <FH>;
  167. chomp($decl);
  168. if (not $decl =~ /\A\s*extern\s+DECLSPEC/) {
  169. #print "Found doxygen but no function sig:\n$str\n\n";
  170. foreach (@templines) {
  171. push @contents, $_;
  172. }
  173. push @contents, $decl;
  174. next;
  175. }
  176. my @decllines = ( $decl );
  177. if (not $decl =~ /\)\s*;/) {
  178. while (<FH>) {
  179. chomp;
  180. push @decllines, $_;
  181. s/\A\s+//;
  182. s/\s+\Z//;
  183. $decl .= " $_";
  184. last if /\)\s*;/;
  185. }
  186. }
  187. $decl =~ s/\s+\);\Z/);/;
  188. $decl =~ s/\s+\Z//;
  189. #print("DECL: [$decl]\n");
  190. my $fn = '';
  191. if ($decl =~ /\A\s*extern\s+DECLSPEC\s+(const\s+|)(unsigned\s+|)(.*?)\s*(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) {
  192. $fn = $5;
  193. #$decl =~ s/\A\s*extern\s+DECLSPEC\s+(.*?)\s+SDLCALL/$1/;
  194. } else {
  195. #print "Found doxygen but no function sig:\n$str\n\n";
  196. foreach (@templines) {
  197. push @contents, $_;
  198. }
  199. foreach (@decllines) {
  200. push @contents, $_;
  201. }
  202. next;
  203. }
  204. $decl = ''; # build this with the line breaks, since it looks better for syntax highlighting.
  205. foreach (@decllines) {
  206. if ($decl eq '') {
  207. $decl = $_;
  208. $decl =~ s/\Aextern\s+DECLSPEC\s+(.*?)\s+(\*?)SDLCALL\s+/$1$2 /;
  209. } else {
  210. my $trimmed = $_;
  211. $trimmed =~ s/\A\s{24}//; # 24 for shrinking to match the removed "extern DECLSPEC SDLCALL "
  212. $decl .= $trimmed;
  213. }
  214. $decl .= "\n";
  215. }
  216. #print("$fn:\n$str\n\n");
  217. $headerfuncs{$fn} = $str;
  218. $headerdecls{$fn} = $decl;
  219. $headerfuncslocation{$fn} = $dent;
  220. $headerfuncschunk{$fn} = scalar(@contents);
  221. push @contents, join("\n", @templines);
  222. push @contents, join("\n", @decllines);
  223. }
  224. close(FH);
  225. $headers{$dent} = \@contents;
  226. }
  227. closedir(DH);
  228. # !!! FIXME: we need to parse enums and typedefs and structs and defines and and and and and...
  229. # !!! FIXME: (but functions are good enough for now.)
  230. my %wikitypes = (); # contains string of wiki page extension, like $wikitypes{"SDL_OpenAudio"} == 'mediawiki'
  231. my %wikifuncs = (); # contains references to hash of strings, each string being the full contents of a section of a wiki page, like $wikifuncs{"SDL_OpenAudio"}{"Remarks"}.
  232. my %wikisectionorder = (); # contains references to array, each array item being a key to a wikipage section in the correct order, like $wikisectionorder{"SDL_OpenAudio"}[2] == 'Remarks'
  233. opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n");
  234. while (readdir(DH)) {
  235. my $dent = $_;
  236. my $type = '';
  237. if ($dent =~ /\ASDL.*?\.(md|mediawiki)\Z/) {
  238. $type = $1;
  239. } else {
  240. next; # only dealing with wiki pages.
  241. }
  242. open(FH, '<', "$wikipath/$dent") or die("Can't open '$wikipath/$dent': $!\n");
  243. my $current_section = '[start]';
  244. my @section_order = ( $current_section );
  245. my $fn = $dent;
  246. $fn =~ s/\..*\Z//;
  247. my %sections = ();
  248. $sections{$current_section} = '';
  249. while (<FH>) {
  250. chomp;
  251. my $orig = $_;
  252. s/\A\s*//;
  253. s/\s*\Z//;
  254. if ($type eq 'mediawiki') {
  255. if (/\A\= (.*?) \=\Z/) {
  256. $current_section = ($1 eq $fn) ? '[Brief]' : $1;
  257. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  258. push @section_order, $current_section;
  259. $sections{$current_section} = '';
  260. } elsif (/\A\=\= (.*?) \=\=\Z/) {
  261. $current_section = ($1 eq $fn) ? '[Brief]' : $1;
  262. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  263. push @section_order, $current_section;
  264. $sections{$current_section} = '';
  265. next;
  266. } elsif (/\A\-\-\-\-\Z/) {
  267. $current_section = '[footer]';
  268. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  269. push @section_order, $current_section;
  270. $sections{$current_section} = '';
  271. next;
  272. }
  273. } elsif ($type eq 'md') {
  274. if (/\A\#+ (.*?)\Z/) {
  275. $current_section = ($1 eq $fn) ? '[Brief]' : $1;
  276. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  277. push @section_order, $current_section;
  278. $sections{$current_section} = '';
  279. next;
  280. } elsif (/\A\-\-\-\-\Z/) {
  281. $current_section = '[footer]';
  282. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  283. push @section_order, $current_section;
  284. $sections{$current_section} = '';
  285. next;
  286. }
  287. } else {
  288. die("Unexpected wiki file type. Fixme!\n");
  289. }
  290. my $str = ($current_section eq 'Code Examples') ? $orig : $_;
  291. $sections{$current_section} .= "$str\n";
  292. }
  293. close(FH);
  294. if (0) {
  295. foreach (@section_order) {
  296. print("$fn SECTION '$_':\n");
  297. print($sections{$_});
  298. print("\n\n");
  299. }
  300. }
  301. $wikitypes{$fn} = $type;
  302. $wikifuncs{$fn} = \%sections;
  303. $wikisectionorder{$fn} = \@section_order;
  304. }
  305. closedir(DH);
  306. if ($warn_about_missing) {
  307. foreach (keys %wikifuncs) {
  308. my $fn = $_;
  309. if (not defined $headerfuncs{$fn}) {
  310. print("WARNING: $fn defined in the wiki but not the headers!\n");
  311. }
  312. }
  313. foreach (keys %headerfuncs) {
  314. my $fn = $_;
  315. if (not defined $wikifuncs{$fn}) {
  316. print("WARNING: $fn defined in the headers but not the wiki!\n");
  317. }
  318. }
  319. }
  320. if ($copy_direction == 1) { # --copy-to-headers
  321. my %changed_headers = ();
  322. # if it's not in the headers already, we don't add it, so iterate what we know is already there for changes.
  323. foreach (keys %headerfuncs) {
  324. my $fn = $_;
  325. next if not defined $wikifuncs{$fn}; # don't have a page for that function, skip it.
  326. my $wikitype = $wikitypes{$fn};
  327. my $sectionsref = $wikifuncs{$fn};
  328. my $remarks = %$sectionsref{'Remarks'};
  329. my $params = %$sectionsref{'Function Parameters'};
  330. my $returns = %$sectionsref{'Return Value'};
  331. my $version = %$sectionsref{'Version'};
  332. my $related = %$sectionsref{'Related Functions'};
  333. my $brief = %$sectionsref{'[Brief]'};
  334. my $addblank = 0;
  335. my $str = '';
  336. $brief = dewikify($wikitype, $brief);
  337. $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  338. my @briefsplit = split /\n/, $brief;
  339. $brief = shift @briefsplit;
  340. if (defined $remarks) {
  341. $remarks = join("\n", @briefsplit) . dewikify($wikitype, $remarks);
  342. }
  343. if (defined $brief) {
  344. $str .= "\n" if $addblank; $addblank = 1;
  345. $str .= wordwrap($brief) . "\n";
  346. }
  347. if (defined $remarks) {
  348. $str .= "\n" if $addblank; $addblank = 1;
  349. $str .= wordwrap($remarks) . "\n";
  350. }
  351. if (defined $params) {
  352. $str .= "\n" if $addblank; $addblank = (defined $returns) ? 0 : 1;
  353. my @lines = split /\n/, dewikify($wikitype, $params);
  354. if ($wikitype eq 'mediawiki') {
  355. die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start
  356. while (scalar(@lines) >= 3) {
  357. my $name = shift @lines;
  358. my $desc = shift @lines;
  359. my $terminator = shift @lines; # the '|-' or '|}' line.
  360. last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table.
  361. $name =~ s/\A\|\s*//;
  362. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  363. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  364. $desc =~ s/\A\|\s*//;
  365. #print STDERR "FN: $fn NAME: $name DESC: $desc TERM: $terminator\n";
  366. my $whitespacelen = length($name) + 8;
  367. my $whitespace = ' ' x $whitespacelen;
  368. $desc = wordwrap($desc, -$whitespacelen);
  369. my @desclines = split /\n/, $desc;
  370. my $firstline = shift @desclines;
  371. $str .= "\\param $name $firstline\n";
  372. foreach (@desclines) {
  373. $str .= "${whitespace}$_\n";
  374. }
  375. }
  376. } else {
  377. die("write me");
  378. }
  379. }
  380. if (defined $returns) {
  381. $str .= "\n" if $addblank; $addblank = 1;
  382. my $r = dewikify($wikitype, $returns);
  383. my $retstr = "\\returns";
  384. if ($r =~ s/\AReturn(s?) //) {
  385. $retstr = "\\return$1";
  386. }
  387. my $whitespacelen = length($retstr) + 1;
  388. my $whitespace = ' ' x $whitespacelen;
  389. $r = wordwrap($r, -$whitespacelen);
  390. my @desclines = split /\n/, $r;
  391. my $firstline = shift @desclines;
  392. $str .= "$retstr $firstline\n";
  393. foreach (@desclines) {
  394. $str .= "${whitespace}$_\n";
  395. }
  396. }
  397. if (defined $version) {
  398. # !!! FIXME: lots of code duplication in all of these.
  399. $str .= "\n" if $addblank; $addblank = 1;
  400. my $v = dewikify($wikitype, $version);
  401. my $whitespacelen = length("\\since") + 1;
  402. my $whitespace = ' ' x $whitespacelen;
  403. $v = wordwrap($v, -$whitespacelen);
  404. my @desclines = split /\n/, $v;
  405. my $firstline = shift @desclines;
  406. $str .= "\\since $firstline\n";
  407. foreach (@desclines) {
  408. $str .= "${whitespace}$_\n";
  409. }
  410. }
  411. if (defined $related) {
  412. # !!! FIXME: lots of code duplication in all of these.
  413. $str .= "\n" if $addblank; $addblank = 1;
  414. my $v = dewikify($wikitype, $related);
  415. my @desclines = split /\n/, $v;
  416. foreach (@desclines) {
  417. s/\A(\:|\* )//;
  418. $str .= "\\sa $_\n";
  419. }
  420. }
  421. my @lines = split /\n/, $str;
  422. my $output = "/**\n";
  423. foreach (@lines) {
  424. chomp;
  425. s/\s*\Z//;
  426. if ($_ eq '') {
  427. $output .= " *\n";
  428. } else {
  429. $output .= " * $_\n";
  430. }
  431. }
  432. $output .= " */";
  433. #print("$fn:\n$output\n\n");
  434. my $header = $headerfuncslocation{$fn};
  435. my $chunk = $headerfuncschunk{$fn};
  436. my $contentsref = $headers{$header};
  437. $$contentsref[$chunk] = $output;
  438. #$$contentsref[$chunk+1] = $headerdecls{$fn};
  439. $changed_headers{$header} = 1;
  440. }
  441. foreach (keys %changed_headers) {
  442. my $contentsref = $headers{$_};
  443. my $path = "$incpath/$_.tmp";
  444. open(FH, '>', $path) or die("Can't open '$path': $!\n");
  445. foreach (@$contentsref) {
  446. print FH "$_\n";
  447. }
  448. close(FH);
  449. rename($path, "$incpath/$_") or die("Can't rename '$path' to '$incpath/$_': $!\n");
  450. }
  451. } elsif ($copy_direction == -1) { # --copy-to-wiki
  452. foreach (keys %headerfuncs) {
  453. my $fn = $_;
  454. my $wikitype = defined $wikitypes{$fn} ? $wikitypes{$fn} : 'md'; # default to Markdown for new stuff.
  455. die("Unexpected wikitype '$wikitype'\n") if (($wikitype ne 'mediawiki') and ($wikitype ne 'md'));
  456. my $raw = $headerfuncs{$fn}; # raw doxygen text with comment characters stripped from start/end and start of each line.
  457. $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.
  458. my @doxygenlines = split /\n/, $raw;
  459. my $brief = '';
  460. while (@doxygenlines) {
  461. last if $doxygenlines[0] =~ /\A\\/; # some sort of doxygen command, assume we're past the general remarks.
  462. last if $doxygenlines[0] =~ /\A\s*\Z/; # blank line? End of paragraph, done.
  463. my $l = shift @doxygenlines;
  464. chomp($l);
  465. $l =~ s/\A\s*//;
  466. $l =~ s/\s*\Z//;
  467. $brief .= "$l ";
  468. }
  469. $brief =~ s/\A(.*?\.) /$1\n\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  470. my @briefsplit = split /\n/, $brief;
  471. $brief = wikify($wikitype, shift @briefsplit);
  472. @doxygenlines = (@briefsplit, @doxygenlines);
  473. my $remarks = '';
  474. while (@doxygenlines) {
  475. last if $doxygenlines[0] =~ /\A\\/; # some sort of doxygen command, assume we're past the general remarks.
  476. my $l = shift @doxygenlines;
  477. $l =~ s/\A\s*//;
  478. $l =~ s/\s*\Z//;
  479. $remarks .= "$l\n";
  480. }
  481. $remarks = wordwrap(wikify($wikitype, $remarks));
  482. $remarks =~ s/\A\s*//;
  483. $remarks =~ s/\s*\Z//;
  484. my $decl = $headerdecls{$fn};
  485. #$decl =~ s/\*\s+SDLCALL/ *SDLCALL/; # Try to make "void * Function" become "void *Function"
  486. #$decl =~ s/\A\s*extern\s+DECLSPEC\s+(.*?)\s+(\*?)SDLCALL/$1$2/;
  487. my $syntax = '';
  488. if ($wikitype eq 'mediawiki') {
  489. $syntax = "<syntaxhighlight lang='c'>\n$decl</syntaxhighlight>\n";
  490. } elsif ($wikitype eq 'md') {
  491. $syntax = "```c\n$decl\n```\n";
  492. } else { die("Expected wikitype '$wikitype'\n"); }
  493. my %sections = ();
  494. $sections{'[Brief]'} = $brief; # include this section even if blank so we get a title line.
  495. $sections{'Remarks'} = "$remarks\n" if $remarks ne '';
  496. $sections{'Syntax'} = $syntax;
  497. my @params = (); # have to parse these and build up the wiki tables after, since Markdown needs to know the length of the largest string. :/
  498. while (@doxygenlines) {
  499. my $l = shift @doxygenlines;
  500. if ($l =~ /\A\\param\s+(.*?)\s+(.*)\Z/) {
  501. my $arg = $1;
  502. my $desc = $2;
  503. while (@doxygenlines) {
  504. my $subline = $doxygenlines[0];
  505. $subline =~ s/\A\s*//;
  506. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  507. last if $subline eq ''; # empty line, this param is done.
  508. shift @doxygenlines; # dump this line from the array; we're using it.
  509. $desc .= wikify($wikitype, " $subline");
  510. }
  511. # We need to know the length of the longest string to make Markdown tables, so we just store these off until everything is parsed.
  512. push @params, $arg;
  513. push @params, $desc;
  514. } elsif ($l =~ /\A\\r(eturns?)\s+(.*)\Z/) {
  515. my $retstr = "R$1"; # "Return" or "Returns"
  516. my $desc = $2;
  517. while (@doxygenlines) {
  518. my $subline = $doxygenlines[0];
  519. $subline =~ s/\A\s*//;
  520. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  521. last if $subline eq ''; # empty line, this param is done.
  522. shift @doxygenlines; # dump this line from the array; we're using it.
  523. $desc .= wikify($wikitype, " $subline");
  524. }
  525. $sections{'Return Value'} = wordwrap("$retstr $desc") . "\n";
  526. } elsif ($l =~ /\A\\since\s+(.*)\Z/) {
  527. my $desc = $1;
  528. while (@doxygenlines) {
  529. my $subline = $doxygenlines[0];
  530. $subline =~ s/\A\s*//;
  531. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  532. last if $subline eq ''; # empty line, this param is done.
  533. shift @doxygenlines; # dump this line from the array; we're using it.
  534. $desc .= wikify($wikitype, " $subline");
  535. }
  536. $sections{'Version'} = wordwrap($desc) . "\n";
  537. } elsif ($l =~ /\A\\sa\s+(.*)\Z/) {
  538. my $sa = $1;
  539. $sections{'Related Functions'} = '' if not defined $sections{'Related Functions'};
  540. if ($wikitype eq 'mediawiki') {
  541. $sections{'Related Functions'} .= ":[[$sa]]\n";
  542. } elsif ($wikitype eq 'md') {
  543. $sections{'Related Functions'} .= "* [$sa](/$sa)\n";
  544. } else { die("Expected wikitype '$wikitype'\n"); }
  545. }
  546. }
  547. # We can build the wiki table now that we have all the data.
  548. if (scalar(@params) > 0) {
  549. my $str = '';
  550. if ($wikitype eq 'mediawiki') {
  551. while (scalar(@params) > 0) {
  552. my $arg = shift @params;
  553. my $desc = shift @params;
  554. $str .= ($str eq '') ? "{|\n" : "|-\n";
  555. $str .= "|'''$arg'''\n";
  556. $str .= "|$desc\n";
  557. }
  558. $str .= "|}\n";
  559. } elsif ($wikitype eq 'md') {
  560. my $longest_arg = 0;
  561. my $longest_desc = 0;
  562. my $which = 0;
  563. foreach (@params) {
  564. my $len = length($_);
  565. if ($which == 0) {
  566. $longest_arg = $len if ($len > $longest_arg);
  567. $which = 1;
  568. } else {
  569. $longest_desc = $len if ($len > $longest_desc);
  570. $which = 0;
  571. }
  572. }
  573. # Markdown tables are sort of obnoxious.
  574. $str .= '| ' . (' ' x ($longest_arg+4)) . ' | ' . (' ' x $longest_desc) . " |\n";
  575. $str .= '| ' . ('-' x ($longest_arg+4)) . ' | ' . ('-' x $longest_desc) . " |\n";
  576. while (@params) {
  577. my $arg = shift @params;
  578. my $desc = shift @params;
  579. $str .= "| **$arg** " . (' ' x ($longest_arg - length($arg))) . "| $desc" . (' ' x ($longest_desc - length($desc))) . " |\n";
  580. }
  581. } else {
  582. die("Unexpected wikitype!\n"); # should have checked this elsewhere.
  583. }
  584. $sections{'Function Parameters'} = $str;
  585. }
  586. my $path = "$wikipath/$_.${wikitype}.tmp";
  587. open(FH, '>', $path) or die("Can't open '$path': $!\n");
  588. my $sectionsref = $wikifuncs{$fn};
  589. foreach (@standard_wiki_sections) {
  590. # drop sections we either replaced or removed from the original wiki's contents.
  591. delete($$sectionsref{$_});
  592. }
  593. my $wikisectionorderref = $wikisectionorder{$fn};
  594. my @ordered_sections = (@standard_wiki_sections, defined $wikisectionorderref ? @$wikisectionorderref : ()); # this copies the arrays into one.
  595. foreach (@ordered_sections) {
  596. my $sect = $_;
  597. next if $sect eq '[start]';
  598. next if (not defined $sections{$sect} and not defined $$sectionsref{$sect});
  599. my $section = defined $sections{$sect} ? $sections{$sect} : $$sectionsref{$sect};
  600. if ($sect eq '[footer]') {
  601. print FH "----\n"; # It's the same in Markdown and MediaWiki.
  602. } elsif ($sect eq '[Brief]') {
  603. if ($wikitype eq 'mediawiki') {
  604. print FH "= $fn =\n\n";
  605. } elsif ($wikitype eq 'md') {
  606. print FH "# $fn\n\n";
  607. } else { die("Expected wikitype '$wikitype'\n"); }
  608. } else {
  609. if ($wikitype eq 'mediawiki') {
  610. print FH "\n== $sect ==\n\n";
  611. } elsif ($wikitype eq 'md') {
  612. print FH "\n## $sect\n\n";
  613. } else { die("Expected wikitype '$wikitype'\n"); }
  614. }
  615. print FH defined $sections{$sect} ? $sections{$sect} : $$sectionsref{$sect};
  616. # make sure these don't show up twice.
  617. delete($sections{$sect});
  618. delete($$sectionsref{$sect});
  619. }
  620. print FH "\n\n";
  621. close(FH);
  622. rename($path, "$wikipath/$_.${wikitype}") or die("Can't rename '$path' to '$wikipath/$_.${wikitype}': $!\n");
  623. }
  624. }
  625. # end of wikiheaders.pl ...