wikiheaders.pl 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167
  1. #!/usr/bin/perl -w
  2. use warnings;
  3. use strict;
  4. use File::Path;
  5. use Text::Wrap;
  6. $Text::Wrap::huge = 'overflow';
  7. my $projectfullname = 'Simple Directmedia Layer';
  8. my $projectshortname = 'SDL';
  9. my $wikisubdir = '';
  10. my $incsubdir = 'include';
  11. my $readmesubdir = undef;
  12. my $apiprefixregex = undef;
  13. my $versionfname = 'include/SDL_version.h';
  14. my $versionmajorregex = '\A\#define\s+SDL_MAJOR_VERSION\s+(\d+)\Z';
  15. my $versionminorregex = '\A\#define\s+SDL_MINOR_VERSION\s+(\d+)\Z';
  16. my $versionpatchregex = '\A\#define\s+SDL_PATCHLEVEL\s+(\d+)\Z';
  17. my $mainincludefname = 'SDL.h';
  18. my $selectheaderregex = '\ASDL.*?\.h\Z';
  19. my $projecturl = 'https://libsdl.org/';
  20. my $wikiurl = 'https://wiki.libsdl.org';
  21. my $bugreporturl = 'https://github.com/libsdl-org/sdlwiki/issues/new';
  22. my $srcpath = undef;
  23. my $wikipath = undef;
  24. my $wikireadmesubdir = 'README';
  25. my $warn_about_missing = 0;
  26. my $copy_direction = 0;
  27. my $optionsfname = undef;
  28. my $wikipreamble = undef;
  29. my $wikiheaderfiletext = 'Defined in %fname%';
  30. my $manpageheaderfiletext = 'Defined in %fname%';
  31. my $changeformat = undef;
  32. my $manpath = undef;
  33. my $gitrev = undef;
  34. foreach (@ARGV) {
  35. $warn_about_missing = 1, next if $_ eq '--warn-about-missing';
  36. $copy_direction = 1, next if $_ eq '--copy-to-headers';
  37. $copy_direction = 1, next if $_ eq '--copy-to-header';
  38. $copy_direction = -1, next if $_ eq '--copy-to-wiki';
  39. $copy_direction = -2, next if $_ eq '--copy-to-manpages';
  40. $copy_direction = -3, next if $_ eq '--report-coverage-gaps';
  41. if (/\A--options=(.*)\Z/) {
  42. $optionsfname = $1;
  43. next;
  44. } elsif (/\A--changeformat=(.*)\Z/) {
  45. $changeformat = $1;
  46. next;
  47. } elsif (/\A--manpath=(.*)\Z/) {
  48. $manpath = $1;
  49. next;
  50. } elsif (/\A--rev=(.*)\Z/) {
  51. $gitrev = $1;
  52. next;
  53. }
  54. $srcpath = $_, next if not defined $srcpath;
  55. $wikipath = $_, next if not defined $wikipath;
  56. }
  57. my $default_optionsfname = '.wikiheaders-options';
  58. $default_optionsfname = "$srcpath/$default_optionsfname" if defined $srcpath;
  59. if ((not defined $optionsfname) && (-f $default_optionsfname)) {
  60. $optionsfname = $default_optionsfname;
  61. }
  62. if (defined $optionsfname) {
  63. open OPTIONS, '<', $optionsfname or die("Failed to open options file '$optionsfname': $!\n");
  64. while (<OPTIONS>) {
  65. chomp;
  66. if (/\A(.*?)\=(.*)\Z/) {
  67. my $key = $1;
  68. my $val = $2;
  69. $key =~ s/\A\s+//;
  70. $key =~ s/\s+\Z//;
  71. $val =~ s/\A\s+//;
  72. $val =~ s/\s+\Z//;
  73. $warn_about_missing = int($val), next if $key eq 'warn_about_missing';
  74. $srcpath = $val, next if $key eq 'srcpath';
  75. $wikipath = $val, next if $key eq 'wikipath';
  76. $apiprefixregex = $val, next if $key eq 'apiprefixregex';
  77. $projectfullname = $val, next if $key eq 'projectfullname';
  78. $projectshortname = $val, next if $key eq 'projectshortname';
  79. $wikisubdir = $val, next if $key eq 'wikisubdir';
  80. $incsubdir = $val, next if $key eq 'incsubdir';
  81. $readmesubdir = $val, next if $key eq 'readmesubdir';
  82. $versionmajorregex = $val, next if $key eq 'versionmajorregex';
  83. $versionminorregex = $val, next if $key eq 'versionminorregex';
  84. $versionpatchregex = $val, next if $key eq 'versionpatchregex';
  85. $versionfname = $val, next if $key eq 'versionfname';
  86. $mainincludefname = $val, next if $key eq 'mainincludefname';
  87. $selectheaderregex = $val, next if $key eq 'selectheaderregex';
  88. $projecturl = $val, next if $key eq 'projecturl';
  89. $wikiurl = $val, next if $key eq 'wikiurl';
  90. $bugreporturl = $val, next if $key eq 'bugreporturl';
  91. $wikipreamble = $val, next if $key eq 'wikipreamble';
  92. $wikiheaderfiletext = $val, next if $key eq 'wikiheaderfiletext';
  93. $manpageheaderfiletext = $val, next if $key eq 'manpageheaderfiletext';
  94. }
  95. }
  96. close(OPTIONS);
  97. }
  98. my $wordwrap_mode = 'mediawiki';
  99. sub wordwrap_atom { # don't call this directly.
  100. my $str = shift;
  101. my $retval = '';
  102. # wordwrap but leave links intact, even if they overflow.
  103. if ($wordwrap_mode eq 'mediawiki') {
  104. while ($str =~ s/(.*?)\s*(\[https?\:\/\/.*?\s+.*?\])\s*//ms) {
  105. $retval .= fill('', '', $1); # wrap it.
  106. $retval .= "\n$2\n"; # don't wrap it.
  107. }
  108. } elsif ($wordwrap_mode eq 'md') {
  109. while ($str =~ s/(.*?)\s*(\[.*?\]\(https?\:\/\/.*?\))\s*//ms) {
  110. $retval .= fill('', '', $1); # wrap it.
  111. $retval .= "\n$2\n"; # don't wrap it.
  112. }
  113. }
  114. return $retval . fill('', '', $str);
  115. }
  116. sub wordwrap_with_bullet_indent { # don't call this directly.
  117. my $bullet = shift;
  118. my $str = shift;
  119. my $retval = '';
  120. #print("WORDWRAP BULLET ('$bullet'):\n\n$str\n\n");
  121. # You _can't_ (at least with Pandoc) have a bullet item with a newline in
  122. # MediaWiki, so _remove_ wrapping!
  123. if ($wordwrap_mode eq 'mediawiki') {
  124. $retval = "$bullet$str";
  125. $retval =~ s/\n/ /gms;
  126. $retval =~ s/\s+$//gms;
  127. #print("WORDWRAP BULLET DONE:\n\n$retval\n\n");
  128. return "$retval\n";
  129. }
  130. my $bulletlen = length($bullet);
  131. # wrap it and then indent each line to be under the bullet.
  132. $Text::Wrap::columns -= $bulletlen;
  133. my @wrappedlines = split /\n/, wordwrap_atom($str);
  134. $Text::Wrap::columns += $bulletlen;
  135. my $prefix = $bullet;
  136. my $usual_prefix = ' ' x $bulletlen;
  137. foreach (@wrappedlines) {
  138. s/\s*\Z//;
  139. $retval .= "$prefix$_\n";
  140. $prefix = $usual_prefix;
  141. }
  142. return $retval;
  143. }
  144. sub wordwrap_one_paragraph { # don't call this directly.
  145. my $retval = '';
  146. my $p = shift;
  147. #print "\n\n\nPARAGRAPH: [$p]\n\n\n";
  148. if ($p =~ s/\A([\*\-] )//) { # bullet list, starts with "* " or "- ".
  149. my $bullet = $1;
  150. my $item = '';
  151. my @items = split /\n/, $p;
  152. foreach (@items) {
  153. if (s/\A([\*\-] )//) {
  154. $retval .= wordwrap_with_bullet_indent($bullet, $item);
  155. $item = '';
  156. }
  157. s/\A\s*//;
  158. $item .= "$_\n"; # accumulate lines until we hit the end or another bullet.
  159. }
  160. if ($item ne '') {
  161. $retval .= wordwrap_with_bullet_indent($bullet, $item);
  162. }
  163. } elsif ($p =~ /\A\s*\|.*\|\s*\n/) { # Markdown table
  164. $retval = "$p\n"; # don't wrap it (!!! FIXME: but maybe parse by lines until we run out of table...)
  165. } else {
  166. $retval = wordwrap_atom($p) . "\n";
  167. }
  168. return $retval;
  169. }
  170. sub wordwrap_paragraphs { # don't call this directly.
  171. my $str = shift;
  172. my $retval = '';
  173. my @paragraphs = split /\n\n/, $str;
  174. foreach (@paragraphs) {
  175. next if $_ eq '';
  176. $retval .= wordwrap_one_paragraph($_);
  177. $retval .= "\n";
  178. }
  179. return $retval;
  180. }
  181. my $wordwrap_default_columns = 76;
  182. sub wordwrap {
  183. my $str = shift;
  184. my $columns = shift;
  185. $columns = $wordwrap_default_columns if not defined $columns;
  186. $columns += $wordwrap_default_columns if $columns < 0;
  187. $Text::Wrap::columns = $columns;
  188. my $retval = '';
  189. #print("\n\nWORDWRAP:\n\n$str\n\n\n");
  190. $str =~ s/\A\n+//ms;
  191. while ($str =~ s/(.*?)(\`\`\`.*?\`\`\`|\<syntaxhighlight.*?\<\/syntaxhighlight\>)//ms) {
  192. #print("\n\nWORDWRAP BLOCK:\n\n$1\n\n ===\n\n$2\n\n\n");
  193. $retval .= wordwrap_paragraphs($1); # wrap it.
  194. $retval .= "$2\n\n"; # don't wrap it.
  195. }
  196. $retval .= wordwrap_paragraphs($str); # wrap what's left.
  197. $retval =~ s/\n+\Z//ms;
  198. #print("\n\nWORDWRAP DONE:\n\n$retval\n\n\n");
  199. return $retval;
  200. }
  201. # This assumes you're moving from Markdown (in the Doxygen data) to Wiki, which
  202. # is why the 'md' section is so sparse.
  203. sub wikify_chunk {
  204. my $wikitype = shift;
  205. my $str = shift;
  206. my $codelang = shift;
  207. my $code = shift;
  208. #print("\n\nWIKIFY CHUNK:\n\n$str\n\n\n");
  209. if ($wikitype eq 'mediawiki') {
  210. # convert `code` things first, so they aren't mistaken for other markdown items.
  211. my $codedstr = '';
  212. while ($str =~ s/\A(.*?)\`(.*?)\`//ms) {
  213. my $codeblock = $2;
  214. $codedstr .= wikify_chunk($wikitype, $1, undef, undef);
  215. if (defined $apiprefixregex) {
  216. # Convert obvious API things to wikilinks, even inside `code` blocks.
  217. $codeblock =~ s/\b($apiprefixregex[a-zA-Z0-9_]+)/[[$1]]/gms;
  218. }
  219. $codedstr .= "<code>$codeblock</code>";
  220. }
  221. # Convert obvious API things to wikilinks.
  222. if (defined $apiprefixregex) {
  223. $str =~ s/\b($apiprefixregex[a-zA-Z0-9_]+)/[[$1]]/gms;
  224. }
  225. # Make some Markdown things into MediaWiki...
  226. # links
  227. $str =~ s/\[(.*?)\]\((https?\:\/\/.*?)\)/\[$2 $1\]/g;
  228. # bold+italic
  229. $str =~ s/\*\*\*(.*?)\*\*\*/'''''$1'''''/gms;
  230. # bold
  231. $str =~ s/\*\*(.*?)\*\*/'''$1'''/gms;
  232. # italic
  233. $str =~ s/\*(.*?)\*/''$1''/gms;
  234. # bullets
  235. $str =~ s/^\- /* /gm;
  236. $str = $codedstr . $str;
  237. if (defined $code) {
  238. $str .= "<syntaxhighlight lang='$codelang'>$code<\/syntaxhighlight>";
  239. }
  240. } elsif ($wikitype eq 'md') {
  241. # convert `code` things first, so they aren't mistaken for other markdown items.
  242. my $codedstr = '';
  243. while ($str =~ s/\A(.*?)(\`.*?\`)//ms) {
  244. my $codeblock = $2;
  245. $codedstr .= wikify_chunk($wikitype, $1, undef, undef);
  246. if (defined $apiprefixregex) {
  247. # Convert obvious API things to wikilinks, even inside `code` blocks,
  248. # BUT ONLY IF the entire code block is the API thing,
  249. # So something like "just call `SDL_Whatever`" will become
  250. # "just call [`SDL_Whatever`](SDL_Whatever)", but
  251. # "just call `SDL_Whatever(7)`" will not. It's just the safest
  252. # way to do this without resorting to wrapping things in html <code> tags.
  253. $codeblock =~ s/\A\`($apiprefixregex[a-zA-Z0-9_]+)\`\Z/[`$1`]($1)/gms;
  254. }
  255. $codedstr .= $codeblock;
  256. }
  257. # Convert obvious API things to wikilinks.
  258. if (defined $apiprefixregex) {
  259. $str =~ s/\b($apiprefixregex[a-zA-Z0-9_]+)/[$1]($1)/gms;
  260. }
  261. $str = $codedstr . $str;
  262. if (defined $code) {
  263. $str .= "```$codelang$code```";
  264. }
  265. }
  266. #print("\n\nWIKIFY CHUNK DONE:\n\n$str\n\n\n");
  267. return $str;
  268. }
  269. sub wikify {
  270. my $wikitype = shift;
  271. my $str = shift;
  272. my $retval = '';
  273. #print("WIKIFY WHOLE:\n\n$str\n\n\n");
  274. # !!! FIXME: this shouldn't check language but rather if there are
  275. # !!! FIXME: chars immediately after "```" to a newline.
  276. while ($str =~ s/\A(.*?)\`\`\`(c\+\+|c|)(.*?)\`\`\`//ms) {
  277. $retval .= wikify_chunk($wikitype, $1, $2, $3);
  278. }
  279. $retval .= wikify_chunk($wikitype, $str, undef, undef);
  280. #print("WIKIFY WHOLE DONE:\n\n$retval\n\n\n");
  281. return $retval;
  282. }
  283. my $dewikify_mode = 'md';
  284. my $dewikify_manpage_code_indent = 1;
  285. sub dewikify_chunk {
  286. my $wikitype = shift;
  287. my $str = shift;
  288. my $codelang = shift;
  289. my $code = shift;
  290. #print("\n\nDEWIKIFY CHUNK:\n\n$str\n\n\n");
  291. if ($dewikify_mode eq 'md') {
  292. if ($wikitype eq 'mediawiki') {
  293. # Doxygen supports Markdown (and it just simply looks better than MediaWiki
  294. # when looking at the raw headers), so do some conversions here as necessary.
  295. # Dump obvious wikilinks.
  296. if (defined $apiprefixregex) {
  297. $str =~ s/\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]/$1/gms;
  298. }
  299. # links
  300. $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\[$2\]\($1\)/g;
  301. # <code></code> is also popular. :/
  302. $str =~ s/\<code>(.*?)<\/code>/`$1`/gms;
  303. # bold+italic
  304. $str =~ s/'''''(.*?)'''''/***$1***/gms;
  305. # bold
  306. $str =~ s/'''(.*?)'''/**$1**/gms;
  307. # italic
  308. $str =~ s/''(.*?)''/*$1*/gms;
  309. # bullets
  310. $str =~ s/^\* /- /gm;
  311. } elsif ($wikitype eq 'md') {
  312. # Dump obvious wikilinks. The rest can just passthrough.
  313. if (defined $apiprefixregex) {
  314. $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/$1/gms;
  315. }
  316. }
  317. if (defined $code) {
  318. $str .= "```$codelang$code```";
  319. }
  320. } elsif ($dewikify_mode eq 'manpage') {
  321. $str =~ s/\./\\[char46]/gms; # make sure these can't become control codes.
  322. if ($wikitype eq 'mediawiki') {
  323. # Dump obvious wikilinks.
  324. if (defined $apiprefixregex) {
  325. $str =~ s/\s*\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]\s*/\n.BR $1\n/gms;
  326. }
  327. # links
  328. $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\n.URL "$1" "$2"\n/g;
  329. # <code></code> is also popular. :/
  330. $str =~ s/\s*\<code>(.*?)<\/code>\s*/\n.BR $1\n/gms;
  331. # bold+italic (this looks bad, just make it bold).
  332. $str =~ s/\s*'''''(.*?)'''''\s*/\n.B $1\n/gms;
  333. # bold
  334. $str =~ s/\s*'''(.*?)'''\s*/\n.B $1\n/gms;
  335. # italic
  336. $str =~ s/\s*''(.*?)''\s*/\n.I $1\n/gms;
  337. # bullets
  338. $str =~ s/^\* /\n\\\(bu /gm;
  339. } elsif ($wikitype eq 'md') {
  340. # Dump obvious wikilinks.
  341. if (defined $apiprefixregex) {
  342. $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/\n.BR $1\n/gms;
  343. }
  344. # links
  345. $str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\n.URL "$2" "$1"\n/g;
  346. # <code></code> is also popular. :/
  347. $str =~ s/\s*\`(.*?)\`\s*/\n.BR $1\n/gms;
  348. # bold+italic (this looks bad, just make it bold).
  349. $str =~ s/\s*\*\*\*(.*?)\*\*\*\s*/\n.B $1\n/gms;
  350. # bold
  351. $str =~ s/\s*\*\*(.*?)\*\*\s*/\n.B $1\n/gms;
  352. # italic
  353. $str =~ s/\s*\*(.*?)\*\s*/\n.I $1\n/gms;
  354. # bullets
  355. $str =~ s/^\- /\n\\\(bu /gm;
  356. } else {
  357. die("Unexpected wikitype when converting to manpages"); # !!! FIXME: need to handle Markdown wiki pages.
  358. }
  359. if (defined $code) {
  360. $code =~ s/\A\n+//gms;
  361. $code =~ s/\n+\Z//gms;
  362. if ($dewikify_manpage_code_indent) {
  363. $str .= "\n.IP\n"
  364. } else {
  365. $str .= "\n.PP\n"
  366. }
  367. $str .= ".EX\n$code\n.EE\n.PP\n";
  368. }
  369. } else {
  370. die("Unexpected dewikify_mode");
  371. }
  372. #print("\n\nDEWIKIFY CHUNK DONE:\n\n$str\n\n\n");
  373. return $str;
  374. }
  375. sub dewikify {
  376. my $wikitype = shift;
  377. my $str = shift;
  378. return '' if not defined $str;
  379. #print("DEWIKIFY WHOLE:\n\n$str\n\n\n");
  380. $str =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms;
  381. $str =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms;
  382. my $retval = '';
  383. while ($str =~ s/\A(.*?)<syntaxhighlight lang='?(.*?)'?>(.*?)<\/syntaxhighlight\>//ms) {
  384. $retval .= dewikify_chunk($wikitype, $1, $2, $3);
  385. }
  386. $retval .= dewikify_chunk($wikitype, $str, undef, undef);
  387. #print("DEWIKIFY WHOLE DONE:\n\n$retval\n\n\n");
  388. return $retval;
  389. }
  390. sub filecopy {
  391. my $src = shift;
  392. my $dst = shift;
  393. my $endline = shift;
  394. $endline = "\n" if not defined $endline;
  395. open(COPYIN, '<', $src) or die("Failed to open '$src' for reading: $!\n");
  396. open(COPYOUT, '>', $dst) or die("Failed to open '$dst' for writing: $!\n");
  397. while (<COPYIN>) {
  398. chomp;
  399. s/[ \t\r\n]*\Z//;
  400. print COPYOUT "$_$endline";
  401. }
  402. close(COPYOUT);
  403. close(COPYIN);
  404. }
  405. sub usage {
  406. 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");
  407. }
  408. usage() if not defined $srcpath;
  409. usage() if not defined $wikipath;
  410. #usage() if $copy_direction == 0;
  411. if (not defined $manpath) {
  412. $manpath = "$srcpath/man";
  413. }
  414. my @standard_wiki_sections = (
  415. 'Draft',
  416. '[Brief]',
  417. 'Deprecated',
  418. 'Header File',
  419. 'Syntax',
  420. 'Function Parameters',
  421. 'Macro Parameters',
  422. 'Fields',
  423. 'Values',
  424. 'Return Value',
  425. 'Remarks',
  426. 'Thread Safety',
  427. 'Version',
  428. 'Code Examples',
  429. 'See Also'
  430. );
  431. # Sections that only ever exist in the wiki and shouldn't be deleted when
  432. # not found in the headers.
  433. my %only_wiki_sections = ( # The ones don't mean anything, I just need to check for key existence.
  434. 'Draft', 1,
  435. 'Code Examples', 1,
  436. 'Header File', 1
  437. );
  438. my %headers = (); # $headers{"SDL_audio.h"} -> reference to an array of all lines of text in SDL_audio.h.
  439. my %headersyms = (); # $headersyms{"SDL_OpenAudio"} -> string of header documentation for SDL_OpenAudio, with comment '*' bits stripped from the start. Newlines embedded!
  440. my %headerdecls = ();
  441. my %headersymslocation = (); # $headersymslocation{"SDL_OpenAudio"} -> name of header holding SDL_OpenAudio define ("SDL_audio.h" in this case).
  442. my %headersymschunk = (); # $headersymschunk{"SDL_OpenAudio"} -> offset in array in %headers that should be replaced for this symbol.
  443. my %headersymshasdoxygen = (); # $headersymshasdoxygen{"SDL_OpenAudio"} -> 1 if there was no existing doxygen for this function.
  444. my %headersymstype = (); # $headersymstype{"SDL_OpenAudio"} -> 1 (function), 2 (macro), 3 (struct), 4 (enum), 5 (other typedef)
  445. my %wikitypes = (); # contains string of wiki page extension, like $wikitypes{"SDL_OpenAudio"} == 'mediawiki'
  446. my %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"}.
  447. 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'
  448. my %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.
  449. my @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!
  450. sub add_coverage_gap {
  451. if ($copy_direction == -3) { # --report-coverage-gaps
  452. my $text = shift;
  453. my $dent = shift;
  454. my $lineno = shift;
  455. return if $text =~ /\A\s*\Z/; # skip blank lines
  456. return if $text =~ /\A\s*\#\s*(if|el|endif|include)/; # skip preprocessor floof.
  457. push @coverage_gap, "$dent:$lineno: $text";
  458. }
  459. }
  460. sub print_undocumented_section {
  461. my $fh = shift;
  462. my $typestr = shift;
  463. my $typeval = shift;
  464. print $fh "## $typestr defined in the headers, but not in the wiki\n\n";
  465. my $header_only_sym = 0;
  466. foreach (sort keys %headersyms) {
  467. my $sym = $_;
  468. if ((not defined $wikisyms{$sym}) && ($headersymstype{$sym} == $typeval)) {
  469. print $fh "- [$sym]($sym)\n";
  470. $header_only_sym = 1;
  471. }
  472. }
  473. if (!$header_only_sym) {
  474. print $fh "(none)\n";
  475. }
  476. print $fh "\n";
  477. 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?)
  478. print $fh "## $typestr defined in the wiki, but not in the headers\n\n";
  479. my $wiki_only_sym = 0;
  480. foreach (sort keys %wikisyms) {
  481. my $sym = $_;
  482. if ((not defined $headersyms{$sym}) && ($headersymstype{$sym} == $typeval)) {
  483. print $fh "- [$sym]($sym)\n";
  484. $wiki_only_sym = 1;
  485. }
  486. }
  487. if (!$wiki_only_sym) {
  488. print $fh "(none)\n";
  489. }
  490. print $fh "\n";
  491. }
  492. }
  493. my $incpath = "$srcpath";
  494. $incpath .= "/$incsubdir" if $incsubdir ne '';
  495. my $wikireadmepath = "$wikipath/$wikireadmesubdir";
  496. my $readmepath = undef;
  497. if (defined $readmesubdir) {
  498. $readmepath = "$srcpath/$readmesubdir";
  499. }
  500. opendir(DH, $incpath) or die("Can't opendir '$incpath': $!\n");
  501. while (my $d = readdir(DH)) {
  502. my $dent = $d;
  503. next if not $dent =~ /$selectheaderregex/; # just selected headers.
  504. open(FH, '<', "$incpath/$dent") or die("Can't open '$incpath/$dent': $!\n");
  505. my @contents = ();
  506. my $ignoring_lines = 0;
  507. my $header_comment = -1;
  508. my $lineno = 0;
  509. while (<FH>) {
  510. chomp;
  511. $lineno++;
  512. my $symtype = 0; # nothing, yet.
  513. my $decl;
  514. my @templines;
  515. my $str;
  516. my $has_doxygen = 1;
  517. # 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.
  518. # Functions and other things are a different story, though!
  519. if ($header_comment == -1) {
  520. $header_comment = /\A\/\*\s*\Z/ ? 1 : 0;
  521. } elsif (($header_comment == 1) && (/\A\*\/\s*\Z/)) {
  522. $header_comment = 0;
  523. }
  524. if ($ignoring_lines && /\A\s*\#\s*endif\s*\Z/) {
  525. $ignoring_lines = 0;
  526. push @contents, $_;
  527. next;
  528. } elsif ($ignoring_lines) {
  529. push @contents, $_;
  530. next;
  531. } elsif (/\A\s*\#\s*ifndef\s+SDL_WIKI_DOCUMENTATION_SECTION\s*\Z/) {
  532. $ignoring_lines = 1;
  533. push @contents, $_;
  534. next;
  535. } elsif (/\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_)?DECLSPEC/) { # a function declaration without a doxygen comment?
  536. $symtype = 1; # function declaration
  537. @templines = ();
  538. $decl = $_;
  539. $str = '';
  540. $has_doxygen = 0;
  541. } elsif (/\A\s*SDL_FORCE_INLINE/) { # a (forced-inline) function declaration without a doxygen comment?
  542. $symtype = 1; # function declaration
  543. @templines = ();
  544. $decl = $_;
  545. $str = '';
  546. $has_doxygen = 0;
  547. } elsif (not /\A\/\*\*\s*\Z/) { # not doxygen comment start?
  548. push @contents, $_;
  549. add_coverage_gap($_, $dent, $lineno) if ($header_comment == 0);
  550. next;
  551. } else { # Start of a doxygen comment, parse it out.
  552. @templines = ( $_ );
  553. while (<FH>) {
  554. chomp;
  555. $lineno++;
  556. push @templines, $_;
  557. last if /\A\s*\*\/\Z/;
  558. 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...
  559. $str .= "$_\n";
  560. while (<FH>) {
  561. chomp;
  562. $lineno++;
  563. push @templines, $_;
  564. s/\A\s*\*\s?//;
  565. if (s/\A\s*\`\`\`/```/) {
  566. $str .= "$_\n";
  567. last;
  568. } else {
  569. $str .= "$_\n";
  570. }
  571. }
  572. } else {
  573. s/\A\s*\*\s*//;
  574. $str .= "$_\n";
  575. }
  576. }
  577. $decl = <FH>;
  578. $lineno++ if defined $decl;
  579. $decl = '' if not defined $decl;
  580. chomp($decl);
  581. if ($decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_)?DECLSPEC/) {
  582. $symtype = 1; # function declaration
  583. } elsif ($decl =~ /\A\s*SDL_FORCE_INLINE/) {
  584. $symtype = 1; # (forced-inline) function declaration
  585. } elsif ($decl =~ /\A\s*\#\s*define\s+/) {
  586. $symtype = 2; # macro
  587. } elsif ($decl =~ /\A\s*(typedef\s+|)(struct|union)/) {
  588. $symtype = 3; # struct or union
  589. } elsif ($decl =~ /\A\s*(typedef\s+|)enum/) {
  590. $symtype = 4; # enum
  591. } elsif ($decl =~ /\A\s*typedef\s+.*;\Z/) {
  592. $symtype = 5; # other typedef
  593. } else {
  594. #print "Found doxygen but no function sig:\n$str\n\n";
  595. foreach (@templines) {
  596. push @contents, $_;
  597. add_coverage_gap($_, $dent, $lineno);
  598. }
  599. push @contents, $decl;
  600. add_coverage_gap($decl, $dent, $lineno);
  601. next;
  602. }
  603. }
  604. my @decllines = ( $decl );
  605. my $sym = '';
  606. if ($symtype == 1) { # a function
  607. my $is_forced_inline = ($decl =~ /\A\s*SDL_FORCE_INLINE/);
  608. if ($is_forced_inline) {
  609. if (not $decl =~ /\)\s*(\{.*|)\s*\Z/) {
  610. while (<FH>) {
  611. chomp;
  612. $lineno++;
  613. push @decllines, $_;
  614. s/\A\s+//;
  615. s/\s+\Z//;
  616. $decl .= " $_";
  617. last if /\)\s*(\{.*|)\s*\Z/;
  618. }
  619. }
  620. $decl =~ s/\s*\)\s*(\{.*|)\s*\Z/);/;
  621. } else {
  622. if (not $decl =~ /\)\s*;/) {
  623. while (<FH>) {
  624. chomp;
  625. $lineno++;
  626. push @decllines, $_;
  627. s/\A\s+//;
  628. s/\s+\Z//;
  629. $decl .= " $_";
  630. last if /\)\s*;/;
  631. }
  632. }
  633. $decl =~ s/\s+\);\Z/);/;
  634. }
  635. $decl =~ s/\s+\Z//;
  636. if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_)?DECLSPEC\s+(const\s+|)(unsigned\s+|)(.*?)\s*(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) {
  637. $sym = $7;
  638. #$decl =~ s/\A\s*extern\s+DECLSPEC\s+(.*?)\s+SDLCALL/$1/;
  639. } elsif ($is_forced_inline && $decl =~ /\A\s*SDL_FORCE_INLINE\s+(SDL_DEPRECATED\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/) {
  640. $sym = $6;
  641. } else {
  642. #print "Found doxygen but no function sig:\n$str\n\n";
  643. foreach (@templines) {
  644. push @contents, $_;
  645. }
  646. foreach (@decllines) {
  647. push @contents, $_;
  648. }
  649. next;
  650. }
  651. if (!$is_forced_inline) { # !!! FIXME: maybe we need to do this for forced-inline stuff too?
  652. $decl = ''; # build this with the line breaks, since it looks better for syntax highlighting.
  653. foreach (@decllines) {
  654. if ($decl eq '') {
  655. $decl = $_;
  656. $decl =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_)?DECLSPEC\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 /;
  657. } else {
  658. my $trimmed = $_;
  659. # !!! FIXME: trim space for SDL_DEPRECATED if it was used, too.
  660. $trimmed =~ s/\A\s{24}//; # 24 for shrinking to match the removed "extern DECLSPEC SDLCALL "
  661. $decl .= $trimmed;
  662. }
  663. $decl .= "\n";
  664. }
  665. }
  666. # !!! FIXME: code duplication with typedef processing, below.
  667. # We assume any `#define`s directly after the function are related to it: probably bitflags for an integer typedef.
  668. # We'll also allow some other basic preprocessor lines.
  669. # Blank lines are allowed, anything else, even comments, are not.
  670. my $blank_lines = 0;
  671. my $lastpos = tell(FH);
  672. my $lastlineno = $lineno;
  673. my $additional_decl = '';
  674. my $saw_define = 0;
  675. while (<FH>) {
  676. chomp;
  677. $lineno++;
  678. if (/\A\s*\Z/) {
  679. $blank_lines++;
  680. } elsif (/\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/) {
  681. if (/\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/) {
  682. $referenceonly{$1} = $sym;
  683. $saw_define = 1;
  684. } elsif (!$saw_define) {
  685. # if the first non-blank thing isn't a #define, assume we're done.
  686. seek(FH, $lastpos, 0); # re-read eaten lines again next time.
  687. $lineno = $lastlineno;
  688. last;
  689. }
  690. # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
  691. if ($blank_lines > 0) {
  692. while ($blank_lines > 0) {
  693. $additional_decl .= "\n";
  694. push @decllines, '';
  695. $blank_lines--;
  696. }
  697. }
  698. $additional_decl .= "\n$_";
  699. push @decllines, $_;
  700. $lastpos = tell(FH);
  701. } else {
  702. seek(FH, $lastpos, 0); # re-read eaten lines again next time.
  703. $lineno = $lastlineno;
  704. last;
  705. }
  706. }
  707. $decl .= $additional_decl;
  708. } elsif ($symtype == 2) { # a macro
  709. if ($decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)\s+/) {
  710. $sym = $1;
  711. #$decl =~ s/\A\s*extern\s+DECLSPEC\s+(.*?)\s+SDLCALL/$1/;
  712. } else {
  713. #print "Found doxygen but no macro:\n$str\n\n";
  714. foreach (@templines) {
  715. push @contents, $_;
  716. }
  717. foreach (@decllines) {
  718. push @contents, $_;
  719. }
  720. next;
  721. }
  722. while ($decl =~ /\\\Z/) {
  723. my $l = <FH>;
  724. last if not $l;
  725. $lineno++;
  726. chomp($l);
  727. push @decllines, $l;
  728. #$l =~ s/\A\s+//;
  729. $l =~ s/\s+\Z//;
  730. $decl .= "\n$l";
  731. }
  732. } elsif (($symtype == 3) || ($symtype == 4)) { # struct or union or enum
  733. my $has_definition = 0;
  734. if ($decl =~ /\A\s*(typedef\s+|)(struct|union|enum)\s*(.*?)\s*(\n|\{|\;|\Z)/) {
  735. my $ctype = $2;
  736. my $origsym = $3;
  737. my $ending = $4;
  738. $sym = $origsym;
  739. if ($sym =~ s/\A(.*?)(\s+)(.*?)\Z/$1/) {
  740. die("Failed to parse '$origsym' correctly!") if ($sym ne $1); # Thought this was "typedef struct MySym MySym;" ... it was not. :( This is a hack!
  741. }
  742. if ($sym eq '') {
  743. die("\n\n$0 FAILURE!\n" .
  744. "There's a 'typedef $ctype' in $incpath/$dent without a name at the top.\n" .
  745. "Instead of `typedef $ctype {} x;`, this should be `typedef $ctype x {} x;`.\n" .
  746. "This causes problems for wikiheaders.pl and scripting language bindings.\n" .
  747. "Please fix it!\n\n");
  748. }
  749. $has_definition = ($ending ne ';');
  750. } else {
  751. #print "Found doxygen but no datatype:\n$str\n\n";
  752. foreach (@templines) {
  753. push @contents, $_;
  754. }
  755. foreach (@decllines) {
  756. push @contents, $_;
  757. }
  758. next;
  759. }
  760. # This block attempts to find the whole struct/union/enum definition by counting matching brackets. Kind of yucky.
  761. if ($has_definition) {
  762. my $started = 0;
  763. my $brackets = 0;
  764. my $pending = $decl;
  765. $decl = '';
  766. while (!$started || ($brackets != 0)) {
  767. foreach my $seg (split(/([{}])/, $pending)) {
  768. $decl .= $seg;
  769. if ($seg eq '{') {
  770. $started = 1;
  771. $brackets++;
  772. } elsif ($seg eq '}') {
  773. die("Something is wrong with header $incpath/$dent while parsing $sym; is a bracket missing?\n") if ($brackets <= 0);
  774. $brackets--;
  775. }
  776. }
  777. if (!$started || ($brackets != 0)) {
  778. $pending = <FH>;
  779. die("EOF/error reading $incpath/$dent while parsing $sym\n") if not $pending;
  780. $lineno++;
  781. chomp($pending);
  782. push @decllines, $pending;
  783. $decl .= "\n";
  784. }
  785. }
  786. # 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!
  787. }
  788. } elsif ($symtype == 5) { # other typedef
  789. if ($decl =~ /\A\s*typedef\s+(.*);\Z/) {
  790. my $tdstr = $1;
  791. #my $datatype;
  792. if ($tdstr =~ /\A(.*?)\s*\((.*?)\s*\*\s*(.*?)\)\s*\((.*?)\)\s*\Z/) { # a function pointer type
  793. $sym = $3;
  794. #$datatype = "$1 ($2 *$sym)($4)";
  795. } elsif ($tdstr =~ /\A(.*[\s\*]+)(.*?)\s*\Z/) {
  796. $sym = $2;
  797. #$datatype = $1;
  798. } else {
  799. die("Failed to parse typedef '$tdstr' in $incpath/$dent!\n"); # I'm hitting a C grammar nail with a regexp hammer here, y'all.
  800. }
  801. $sym =~ s/\A\s+//;
  802. $sym =~ s/\s+\Z//;
  803. #$datatype =~ s/\A\s+//;
  804. #$datatype =~ s/\s+\Z//;
  805. } else {
  806. #print "Found doxygen but no datatype:\n$str\n\n";
  807. foreach (@templines) {
  808. push @contents, $_;
  809. }
  810. foreach (@decllines) {
  811. push @contents, $_;
  812. }
  813. next;
  814. }
  815. # We assume any `#define`s directly after the typedef are related to it: probably bitflags for an integer typedef.
  816. # We'll also allow some other basic preprocessor lines.
  817. # Blank lines are allowed, anything else, even comments, are not.
  818. my $blank_lines = 0;
  819. my $lastpos = tell(FH);
  820. my $lastlineno = $lineno;
  821. my $additional_decl = '';
  822. my $saw_define = 0;
  823. while (<FH>) {
  824. chomp;
  825. $lineno++;
  826. if (/\A\s*\Z/) {
  827. $blank_lines++;
  828. } elsif (/\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/) {
  829. if (/\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/) {
  830. $referenceonly{$1} = $sym;
  831. $saw_define = 1;
  832. } elsif (!$saw_define) {
  833. # if the first non-blank thing isn't a #define, assume we're done.
  834. seek(FH, $lastpos, 0); # re-read eaten lines again next time.
  835. $lineno = $lastlineno;
  836. last;
  837. }
  838. # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
  839. if ($blank_lines > 0) {
  840. while ($blank_lines > 0) {
  841. $additional_decl .= "\n";
  842. push @decllines, '';
  843. $blank_lines--;
  844. }
  845. }
  846. $additional_decl .= "\n$_";
  847. push @decllines, $_;
  848. $lastpos = tell(FH);
  849. } else {
  850. seek(FH, $lastpos, 0); # re-read eaten lines again next time.
  851. $lineno = $lastlineno;
  852. last;
  853. }
  854. }
  855. $decl .= $additional_decl;
  856. } else {
  857. die("Unexpected symtype $symtype");
  858. }
  859. #print("DECL: [$decl]\n");
  860. #print("$sym:\n$str\n\n");
  861. # There might be multiple declarations of a function due to #ifdefs,
  862. # and only one of them will have documentation. If we hit an
  863. # undocumented one before, delete the placeholder line we left for
  864. # it so it doesn't accumulate a new blank line on each run.
  865. my $skipsym = 0;
  866. if (defined $headersymshasdoxygen{$sym}) {
  867. if ($headersymshasdoxygen{$sym} == 0) { # An undocumented declaration already exists, nuke its placeholder line.
  868. delete $contents[$headersymschunk{$sym}]; # delete DOES NOT RENUMBER existing elements!
  869. } else { # documented function already existed?
  870. $skipsym = 1; # don't add this copy to the list of functions.
  871. if ($has_doxygen) {
  872. print STDERR "WARNING: Symbol '$sym' appears to be documented in multiple locations. Only keeping the first one we saw!\n";
  873. }
  874. push @contents, join("\n", @decllines); # just put the existing declation in as-is.
  875. }
  876. }
  877. if (!$skipsym) {
  878. $headersyms{$sym} = $str;
  879. $headerdecls{$sym} = $decl;
  880. $headersymslocation{$sym} = $dent;
  881. $headersymschunk{$sym} = scalar(@contents);
  882. $headersymshasdoxygen{$sym} = $has_doxygen;
  883. $headersymstype{$sym} = $symtype;
  884. push @contents, join("\n", @templines);
  885. push @contents, join("\n", @decllines);
  886. }
  887. }
  888. close(FH);
  889. $headers{$dent} = \@contents;
  890. }
  891. closedir(DH);
  892. opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n");
  893. while (my $d = readdir(DH)) {
  894. my $dent = $d;
  895. my $type = '';
  896. if ($dent =~ /\.(md|mediawiki)\Z/) {
  897. $type = $1;
  898. } else {
  899. next; # only dealing with wiki pages.
  900. }
  901. my $sym = $dent;
  902. $sym =~ s/\..*\Z//;
  903. # Ignore FrontPage.
  904. next if $sym eq 'FrontPage';
  905. # Ignore "Category*" pages.
  906. next if ($sym =~ /\ACategory/);
  907. open(FH, '<', "$wikipath/$dent") or die("Can't open '$wikipath/$dent': $!\n");
  908. my $current_section = '[start]';
  909. my @section_order = ( $current_section );
  910. my %sections = ();
  911. $sections{$current_section} = '';
  912. my $firstline = 1;
  913. while (<FH>) {
  914. chomp;
  915. my $orig = $_;
  916. s/\A\s*//;
  917. s/\s*\Z//;
  918. if ($type eq 'mediawiki') {
  919. if (defined($wikipreamble) && $firstline && /\A\=\=\=\=\=\= (.*?) \=\=\=\=\=\=\Z/ && ($1 eq $wikipreamble)) {
  920. $firstline = 0; # skip this.
  921. next;
  922. } elsif (/\A\= (.*?) \=\Z/) {
  923. $firstline = 0;
  924. $current_section = ($1 eq $sym) ? '[Brief]' : $1;
  925. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  926. push @section_order, $current_section;
  927. $sections{$current_section} = '';
  928. } elsif (/\A\=\= (.*?) \=\=\Z/) {
  929. $firstline = 0;
  930. $current_section = ($1 eq $sym) ? '[Brief]' : $1;
  931. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  932. push @section_order, $current_section;
  933. $sections{$current_section} = '';
  934. next;
  935. } elsif (/\A\-\-\-\-\Z/) {
  936. $firstline = 0;
  937. $current_section = '[footer]';
  938. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  939. push @section_order, $current_section;
  940. $sections{$current_section} = '';
  941. next;
  942. }
  943. } elsif ($type eq 'md') {
  944. if (defined($wikipreamble) && $firstline && /\A\#\#\#\#\#\# (.*?)\Z/ && ($1 eq $wikipreamble)) {
  945. $firstline = 0; # skip this.
  946. next;
  947. } elsif (/\A\#+ (.*?)\Z/) {
  948. $firstline = 0;
  949. $current_section = ($1 eq $sym) ? '[Brief]' : $1;
  950. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  951. push @section_order, $current_section;
  952. $sections{$current_section} = '';
  953. next;
  954. } elsif (/\A\-\-\-\-\Z/) {
  955. $firstline = 0;
  956. $current_section = '[footer]';
  957. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  958. push @section_order, $current_section;
  959. $sections{$current_section} = '';
  960. next;
  961. }
  962. } else {
  963. die("Unexpected wiki file type. Fixme!");
  964. }
  965. if ($firstline) {
  966. $firstline = ($_ ne '');
  967. }
  968. if (!$firstline) {
  969. $sections{$current_section} .= "$orig\n";
  970. }
  971. }
  972. close(FH);
  973. foreach (keys %sections) {
  974. $sections{$_} =~ s/\A\n+//;
  975. $sections{$_} =~ s/\n+\Z//;
  976. $sections{$_} .= "\n";
  977. }
  978. # older section name we used, migrate over from it.
  979. if (defined $sections{'Related Functions'}) {
  980. if (not defined $sections{'See Also'}) {
  981. $sections{'See Also'} = $sections{'Related Functions'};
  982. }
  983. delete $sections{'Related Functions'};
  984. }
  985. if (0) {
  986. foreach (@section_order) {
  987. print("$sym SECTION '$_':\n");
  988. print($sections{$_});
  989. print("\n\n");
  990. }
  991. }
  992. $wikitypes{$sym} = $type;
  993. $wikisyms{$sym} = \%sections;
  994. $wikisectionorder{$sym} = \@section_order;
  995. }
  996. closedir(DH);
  997. delete $wikisyms{"Undocumented"};
  998. {
  999. my $path = "$wikipath/Undocumented.md";
  1000. open(my $fh, '>', $path) or die("Can't open '$path': $!\n");
  1001. print $fh "# Undocumented\n\n";
  1002. print_undocumented_section($fh, 'Functions', 1);
  1003. #print_undocumented_section($fh, 'Macros', 2);
  1004. close($fh);
  1005. }
  1006. if ($warn_about_missing) {
  1007. foreach (keys %wikisyms) {
  1008. my $sym = $_;
  1009. if (not defined $headersyms{$sym}) {
  1010. print("WARNING: $sym defined in the wiki but not the headers!\n");
  1011. }
  1012. }
  1013. foreach (keys %headersyms) {
  1014. my $sym = $_;
  1015. if (not defined $wikisyms{$sym}) {
  1016. print("WARNING: $sym defined in the headers but not the wiki!\n");
  1017. }
  1018. }
  1019. }
  1020. if ($copy_direction == 1) { # --copy-to-headers
  1021. my %changed_headers = ();
  1022. $dewikify_mode = 'md';
  1023. $wordwrap_mode = 'md'; # the headers use Markdown format.
  1024. foreach (keys %headersyms) {
  1025. my $sym = $_;
  1026. next if not defined $wikisyms{$sym}; # don't have a page for that function, skip it.
  1027. my $symtype = $headersymstype{$sym};
  1028. my $wikitype = $wikitypes{$sym};
  1029. my $sectionsref = $wikisyms{$sym};
  1030. my $remarks = $sectionsref->{'Remarks'};
  1031. my $returns = $sectionsref->{'Return Value'};
  1032. my $threadsafety = $sectionsref->{'Thread Safety'};
  1033. my $version = $sectionsref->{'Version'};
  1034. my $related = $sectionsref->{'See Also'};
  1035. my $deprecated = $sectionsref->{'Deprecated'};
  1036. my $brief = $sectionsref->{'[Brief]'};
  1037. my $addblank = 0;
  1038. my $str = '';
  1039. my $params = undef;
  1040. my $paramstr = undef;
  1041. if (($symtype == 1) || (($symtype == 5))) { # we'll assume a typedef (5) with a \param is a function pointer typedef.
  1042. $params = $sectionsref->{'Function Parameters'};
  1043. $paramstr = '\param';
  1044. } elsif ($symtype == 2) {
  1045. $params = $sectionsref->{'Macro Parameters'};
  1046. $paramstr = '\param';
  1047. } elsif ($symtype == 3) {
  1048. $params = $sectionsref->{'Fields'};
  1049. $paramstr = '\field';
  1050. } elsif ($symtype == 4) {
  1051. $params = $sectionsref->{'Values'};
  1052. $paramstr = '\value';
  1053. } else {
  1054. die("Unexpected symtype $symtype");
  1055. }
  1056. $headersymshasdoxygen{$sym} = 1; # Added/changed doxygen for this header.
  1057. $brief = dewikify($wikitype, $brief);
  1058. $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  1059. my @briefsplit = split /\n/, $brief;
  1060. $brief = shift @briefsplit;
  1061. if (defined $remarks) {
  1062. $remarks = join("\n", @briefsplit) . dewikify($wikitype, $remarks);
  1063. }
  1064. if (defined $brief) {
  1065. $str .= "\n" if $addblank; $addblank = 1;
  1066. $str .= wordwrap($brief) . "\n";
  1067. }
  1068. if (defined $remarks) {
  1069. $str .= "\n" if $addblank; $addblank = 1;
  1070. $str .= wordwrap($remarks) . "\n";
  1071. }
  1072. if (defined $deprecated) {
  1073. # !!! FIXME: lots of code duplication in all of these.
  1074. $str .= "\n" if $addblank; $addblank = 1;
  1075. my $v = dewikify($wikitype, $deprecated);
  1076. my $whitespacelen = length("\\deprecated") + 1;
  1077. my $whitespace = ' ' x $whitespacelen;
  1078. $v = wordwrap($v, -$whitespacelen);
  1079. my @desclines = split /\n/, $v;
  1080. my $firstline = shift @desclines;
  1081. $str .= "\\deprecated $firstline\n";
  1082. foreach (@desclines) {
  1083. $str .= "${whitespace}$_\n";
  1084. }
  1085. }
  1086. if (defined $params) {
  1087. $str .= "\n" if $addblank; $addblank = (defined $returns) ? 0 : 1;
  1088. my @lines = split /\n/, dewikify($wikitype, $params);
  1089. if ($wikitype eq 'mediawiki') {
  1090. die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start
  1091. while (scalar(@lines) >= 3) {
  1092. my $name = shift @lines;
  1093. my $desc = shift @lines;
  1094. my $terminator = shift @lines; # the '|-' or '|}' line.
  1095. last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table.
  1096. $name =~ s/\A\|\s*//;
  1097. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  1098. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  1099. $desc =~ s/\A\|\s*//;
  1100. #print STDERR "SYM: $sym NAME: $name DESC: $desc TERM: $terminator\n";
  1101. my $whitespacelen = length($name) + 8;
  1102. my $whitespace = ' ' x $whitespacelen;
  1103. $desc = wordwrap($desc, -$whitespacelen);
  1104. my @desclines = split /\n/, $desc;
  1105. my $firstline = shift @desclines;
  1106. $str .= "$paramstr $name $firstline\n";
  1107. foreach (@desclines) {
  1108. $str .= "${whitespace}$_\n";
  1109. }
  1110. }
  1111. } elsif ($wikitype eq 'md') {
  1112. my $l;
  1113. $l = shift @lines;
  1114. die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*\|\s*\|\s*\|\s*\Z/);
  1115. $l = shift @lines;
  1116. die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/);
  1117. while (scalar(@lines) >= 1) {
  1118. $l = shift @lines;
  1119. if ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) {
  1120. my $name = $1;
  1121. my $desc = $2;
  1122. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  1123. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  1124. #print STDERR "SYM: $sym NAME: $name DESC: $desc\n";
  1125. my $whitespacelen = length($name) + 8;
  1126. my $whitespace = ' ' x $whitespacelen;
  1127. $desc = wordwrap($desc, -$whitespacelen);
  1128. my @desclines = split /\n/, $desc;
  1129. my $firstline = shift @desclines;
  1130. $str .= "$paramstr $name $firstline\n";
  1131. foreach (@desclines) {
  1132. $str .= "${whitespace}$_\n";
  1133. }
  1134. } else {
  1135. last; # we seem to have run out of table.
  1136. }
  1137. }
  1138. } else {
  1139. die("write me");
  1140. }
  1141. }
  1142. if (defined $returns) {
  1143. $str .= "\n" if $addblank; $addblank = 1;
  1144. my $r = dewikify($wikitype, $returns);
  1145. my $retstr = "\\returns";
  1146. if ($r =~ s/\AReturn(s?) //) {
  1147. $retstr = "\\return$1";
  1148. }
  1149. my $whitespacelen = length($retstr) + 1;
  1150. my $whitespace = ' ' x $whitespacelen;
  1151. $r = wordwrap($r, -$whitespacelen);
  1152. my @desclines = split /\n/, $r;
  1153. my $firstline = shift @desclines;
  1154. $str .= "$retstr $firstline\n";
  1155. foreach (@desclines) {
  1156. $str .= "${whitespace}$_\n";
  1157. }
  1158. }
  1159. if (defined $threadsafety) {
  1160. # !!! FIXME: lots of code duplication in all of these.
  1161. $str .= "\n" if $addblank; $addblank = 1;
  1162. my $v = dewikify($wikitype, $threadsafety);
  1163. my $whitespacelen = length("\\threadsafety") + 1;
  1164. my $whitespace = ' ' x $whitespacelen;
  1165. $v = wordwrap($v, -$whitespacelen);
  1166. my @desclines = split /\n/, $v;
  1167. my $firstline = shift @desclines;
  1168. $str .= "\\threadsafety $firstline\n";
  1169. foreach (@desclines) {
  1170. $str .= "${whitespace}$_\n";
  1171. }
  1172. }
  1173. if (defined $version) {
  1174. # !!! FIXME: lots of code duplication in all of these.
  1175. $str .= "\n" if $addblank; $addblank = 1;
  1176. my $v = dewikify($wikitype, $version);
  1177. my $whitespacelen = length("\\since") + 1;
  1178. my $whitespace = ' ' x $whitespacelen;
  1179. $v = wordwrap($v, -$whitespacelen);
  1180. my @desclines = split /\n/, $v;
  1181. my $firstline = shift @desclines;
  1182. $str .= "\\since $firstline\n";
  1183. foreach (@desclines) {
  1184. $str .= "${whitespace}$_\n";
  1185. }
  1186. }
  1187. if (defined $related) {
  1188. # !!! FIXME: lots of code duplication in all of these.
  1189. $str .= "\n" if $addblank; $addblank = 1;
  1190. my $v = dewikify($wikitype, $related);
  1191. my @desclines = split /\n/, $v;
  1192. foreach (@desclines) {
  1193. s/\A(\:|\* )//;
  1194. s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func"
  1195. s/\[\[(.*?)\]\]/$1/; # in case some wikilinks remain.
  1196. s/\[(.*?)\]\(.*?\)/$1/; # in case some wikilinks remain.
  1197. s/\A\/*//;
  1198. $str .= "\\sa $_\n";
  1199. }
  1200. }
  1201. my $header = $headersymslocation{$sym};
  1202. my $contentsref = $headers{$header};
  1203. my $chunk = $headersymschunk{$sym};
  1204. my @lines = split /\n/, $str;
  1205. my $addnewline = (($chunk > 0) && ($$contentsref[$chunk-1] ne '')) ? "\n" : '';
  1206. my $output = "$addnewline/**\n";
  1207. foreach (@lines) {
  1208. chomp;
  1209. s/\s*\Z//;
  1210. if ($_ eq '') {
  1211. $output .= " *\n";
  1212. } else {
  1213. $output .= " * $_\n";
  1214. }
  1215. }
  1216. $output .= " */";
  1217. #print("$sym:\n$output\n\n");
  1218. $$contentsref[$chunk] = $output;
  1219. #$$contentsref[$chunk+1] = $headerdecls{$sym};
  1220. $changed_headers{$header} = 1;
  1221. }
  1222. foreach (keys %changed_headers) {
  1223. my $header = $_;
  1224. # this is kinda inefficient, but oh well.
  1225. my @removelines = ();
  1226. foreach (keys %headersymslocation) {
  1227. my $sym = $_;
  1228. next if $headersymshasdoxygen{$sym};
  1229. next if $headersymslocation{$sym} ne $header;
  1230. # 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.
  1231. push @removelines, $headersymschunk{$sym};
  1232. }
  1233. my $contentsref = $headers{$header};
  1234. foreach (@removelines) {
  1235. delete $$contentsref[$_]; # delete DOES NOT RENUMBER existing elements!
  1236. }
  1237. my $path = "$incpath/$header.tmp";
  1238. open(FH, '>', $path) or die("Can't open '$path': $!\n");
  1239. foreach (@$contentsref) {
  1240. print FH "$_\n" if defined $_;
  1241. }
  1242. close(FH);
  1243. rename($path, "$incpath/$header") or die("Can't rename '$path' to '$incpath/$header': $!\n");
  1244. }
  1245. if (defined $readmepath) {
  1246. if ( -d $wikireadmepath ) {
  1247. mkdir($readmepath); # just in case
  1248. opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n");
  1249. while (readdir(DH)) {
  1250. my $dent = $_;
  1251. if ($dent =~ /\A(.*?)\.md\Z/) { # we only bridge Markdown files here.
  1252. next if $1 eq 'FrontPage';
  1253. filecopy("$wikireadmepath/$dent", "$readmepath/README-$dent", "\n");
  1254. }
  1255. }
  1256. closedir(DH);
  1257. }
  1258. }
  1259. } elsif ($copy_direction == -1) { # --copy-to-wiki
  1260. if (defined $changeformat) {
  1261. $dewikify_mode = $changeformat;
  1262. $wordwrap_mode = $changeformat;
  1263. }
  1264. foreach (keys %headersyms) {
  1265. my $sym = $_;
  1266. next if not $headersymshasdoxygen{$sym};
  1267. my $symtype = $headersymstype{$sym};
  1268. my $origwikitype = defined $wikitypes{$sym} ? $wikitypes{$sym} : 'md'; # default to MarkDown for new stuff.
  1269. my $wikitype = (defined $changeformat) ? $changeformat : $origwikitype;
  1270. die("Unexpected wikitype '$wikitype'") if (($wikitype ne 'mediawiki') and ($wikitype ne 'md') and ($wikitype ne 'manpage'));
  1271. #print("$sym\n"); next;
  1272. $wordwrap_mode = $wikitype;
  1273. my $raw = $headersyms{$sym}; # raw doxygen text with comment characters stripped from start/end and start of each line.
  1274. next if not defined $raw;
  1275. $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.
  1276. my @doxygenlines = split /\n/, $raw;
  1277. my $brief = '';
  1278. while (@doxygenlines) {
  1279. last if $doxygenlines[0] =~ /\A\\/; # some sort of doxygen command, assume we're past the general remarks.
  1280. last if $doxygenlines[0] =~ /\A\s*\Z/; # blank line? End of paragraph, done.
  1281. my $l = shift @doxygenlines;
  1282. chomp($l);
  1283. $l =~ s/\A\s*//;
  1284. $l =~ s/\s*\Z//;
  1285. $brief .= "$l ";
  1286. }
  1287. $brief =~ s/\s+\Z//;
  1288. $brief =~ s/\A(.*?\.) /$1\n\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  1289. my @briefsplit = split /\n/, $brief;
  1290. next if not defined $briefsplit[0]; # No brief text? Probably a bogus Doxygen comment, skip it.
  1291. $brief = wikify($wikitype, shift @briefsplit) . "\n";
  1292. @doxygenlines = (@briefsplit, @doxygenlines);
  1293. my $remarks = '';
  1294. while (@doxygenlines) {
  1295. last if $doxygenlines[0] =~ /\A\\/; # some sort of doxygen command, assume we're past the general remarks.
  1296. my $l = shift @doxygenlines;
  1297. $remarks .= "$l\n";
  1298. }
  1299. #print("REMARKS:\n\n $remarks\n\n");
  1300. $remarks = wordwrap(wikify($wikitype, $remarks));
  1301. $remarks =~ s/\A\s*//;
  1302. $remarks =~ s/\s*\Z//;
  1303. my $decl = $headerdecls{$sym};
  1304. #$decl =~ s/\*\s+SDLCALL/ *SDLCALL/; # Try to make "void * Function" become "void *Function"
  1305. #$decl =~ s/\A\s*extern\s+(SDL_DEPRECATED\s+|)DECLSPEC\s+(.*?)\s+(\*?)SDLCALL/$2$3/;
  1306. my $syntax = '';
  1307. if ($wikitype eq 'mediawiki') {
  1308. $syntax = "<syntaxhighlight lang='c'>\n$decl</syntaxhighlight>\n";
  1309. } elsif ($wikitype eq 'md') {
  1310. $syntax = "```c\n$decl\n```\n";
  1311. } else { die("Expected wikitype '$wikitype'"); }
  1312. my %sections = ();
  1313. $sections{'[Brief]'} = $brief; # include this section even if blank so we get a title line.
  1314. $sections{'Remarks'} = "$remarks\n" if $remarks ne '';
  1315. $sections{'Syntax'} = $syntax;
  1316. my @params = (); # have to parse these and build up the wiki tables after, since Markdown needs to know the length of the largest string. :/
  1317. while (@doxygenlines) {
  1318. my $l = shift @doxygenlines;
  1319. # We allow param/field/value interchangeably, even if it doesn't make sense. The next --copy-to-headers will correct it anyhow.
  1320. if ($l =~ /\A\\(param|field|value)\s+(.*?)\s+(.*)\Z/) {
  1321. my $arg = $2;
  1322. my $desc = $3;
  1323. while (@doxygenlines) {
  1324. my $subline = $doxygenlines[0];
  1325. $subline =~ s/\A\s*//;
  1326. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1327. shift @doxygenlines; # dump this line from the array; we're using it.
  1328. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1329. $desc .= "\n";
  1330. } else {
  1331. $desc .= " $subline";
  1332. }
  1333. }
  1334. $desc =~ s/[\s\n]+\Z//ms;
  1335. # We need to know the length of the longest string to make Markdown tables, so we just store these off until everything is parsed.
  1336. push @params, $arg;
  1337. push @params, $desc;
  1338. } elsif ($l =~ /\A\\r(eturns?)\s+(.*)\Z/) {
  1339. my $retstr = "R$1"; # "Return" or "Returns"
  1340. my $desc = $2;
  1341. while (@doxygenlines) {
  1342. my $subline = $doxygenlines[0];
  1343. $subline =~ s/\A\s*//;
  1344. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1345. shift @doxygenlines; # dump this line from the array; we're using it.
  1346. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1347. $desc .= "\n";
  1348. } else {
  1349. $desc .= " $subline";
  1350. }
  1351. }
  1352. $desc =~ s/[\s\n]+\Z//ms;
  1353. $sections{'Return Value'} = wordwrap("$retstr " . wikify($wikitype, $desc)) . "\n";
  1354. } elsif ($l =~ /\A\\deprecated\s+(.*)\Z/) {
  1355. my $desc = $1;
  1356. while (@doxygenlines) {
  1357. my $subline = $doxygenlines[0];
  1358. $subline =~ s/\A\s*//;
  1359. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1360. shift @doxygenlines; # dump this line from the array; we're using it.
  1361. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1362. $desc .= "\n";
  1363. } else {
  1364. $desc .= " $subline";
  1365. }
  1366. }
  1367. $desc =~ s/[\s\n]+\Z//ms;
  1368. $sections{'Deprecated'} = wordwrap(wikify($wikitype, $desc)) . "\n";
  1369. } elsif ($l =~ /\A\\since\s+(.*)\Z/) {
  1370. my $desc = $1;
  1371. while (@doxygenlines) {
  1372. my $subline = $doxygenlines[0];
  1373. $subline =~ s/\A\s*//;
  1374. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1375. shift @doxygenlines; # dump this line from the array; we're using it.
  1376. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1377. $desc .= "\n";
  1378. } else {
  1379. $desc .= " $subline";
  1380. }
  1381. }
  1382. $desc =~ s/[\s\n]+\Z//ms;
  1383. $sections{'Version'} = wordwrap(wikify($wikitype, $desc)) . "\n";
  1384. } elsif ($l =~ /\A\\threadsafety\s+(.*)\Z/) {
  1385. my $desc = $1;
  1386. while (@doxygenlines) {
  1387. my $subline = $doxygenlines[0];
  1388. $subline =~ s/\A\s*//;
  1389. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1390. shift @doxygenlines; # dump this line from the array; we're using it.
  1391. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1392. $desc .= "\n";
  1393. } else {
  1394. $desc .= " $subline";
  1395. }
  1396. }
  1397. $desc =~ s/[\s\n]+\Z//ms;
  1398. $sections{'Thread Safety'} = wordwrap(wikify($wikitype, $desc)) . "\n";
  1399. } elsif ($l =~ /\A\\sa\s+(.*)\Z/) {
  1400. my $sa = $1;
  1401. $sa =~ s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func"
  1402. $sections{'See Also'} = '' if not defined $sections{'See Also'};
  1403. if ($wikitype eq 'mediawiki') {
  1404. $sections{'See Also'} .= ":[[$sa]]\n";
  1405. } elsif ($wikitype eq 'md') {
  1406. $sections{'See Also'} .= "* [$sa]($sa)\n";
  1407. } else { die("Expected wikitype '$wikitype'"); }
  1408. }
  1409. }
  1410. my $hfiletext = $wikiheaderfiletext;
  1411. $hfiletext =~ s/\%fname\%/$headersymslocation{$sym}/g;
  1412. $sections{'Header File'} = "$hfiletext\n";
  1413. # Make sure this ends with a double-newline.
  1414. $sections{'See Also'} .= "\n" if defined $sections{'See Also'};
  1415. if (0) { # !!! FIXME: this was a useful hack, but this needs to be generalized if we're going to do this always.
  1416. # Plug in a \since section if one wasn't listed.
  1417. if (not defined $sections{'Version'}) {
  1418. my $symtypename;
  1419. if ($symtype == 1) {
  1420. $symtypename = 'function';
  1421. } elsif ($symtype == 2) {
  1422. $symtypename = 'macro';
  1423. } elsif ($symtype == 3) {
  1424. $symtypename = 'struct';
  1425. } elsif ($symtype == 4) {
  1426. $symtypename = 'enum';
  1427. } elsif ($symtype == 5) {
  1428. $symtypename = 'datatype';
  1429. } else {
  1430. die("Unexpected symbol type $symtype!");
  1431. }
  1432. my $str = "This $symtypename is available since SDL 3.0.0.";
  1433. $sections{'Version'} = wordwrap(wikify($wikitype, $str)) . "\n";
  1434. }
  1435. }
  1436. # We can build the wiki table now that we have all the data.
  1437. if (scalar(@params) > 0) {
  1438. my $str = '';
  1439. if ($wikitype eq 'mediawiki') {
  1440. while (scalar(@params) > 0) {
  1441. my $arg = shift @params;
  1442. my $desc = wikify($wikitype, shift @params);
  1443. $str .= ($str eq '') ? "{|\n" : "|-\n";
  1444. $str .= "|'''$arg'''\n";
  1445. $str .= "|$desc\n";
  1446. }
  1447. $str .= "|}\n";
  1448. } elsif ($wikitype eq 'md') {
  1449. my $longest_arg = 0;
  1450. my $longest_desc = 0;
  1451. my $which = 0;
  1452. foreach (@params) {
  1453. if ($which == 0) {
  1454. my $len = length($_) + 4;
  1455. $longest_arg = $len if ($len > $longest_arg);
  1456. $which = 1;
  1457. } else {
  1458. my $len = length(wikify($wikitype, $_));
  1459. $longest_desc = $len if ($len > $longest_desc);
  1460. $which = 0;
  1461. }
  1462. }
  1463. # Markdown tables are sort of obnoxious.
  1464. $str .= '| ' . (' ' x ($longest_arg+4)) . ' | ' . (' ' x $longest_desc) . " |\n";
  1465. $str .= '| ' . ('-' x ($longest_arg+4)) . ' | ' . ('-' x $longest_desc) . " |\n";
  1466. while (@params) {
  1467. my $arg = shift @params;
  1468. my $desc = wikify($wikitype, shift @params);
  1469. $str .= "| **$arg** " . (' ' x ($longest_arg - length($arg))) . "| $desc" . (' ' x ($longest_desc - length($desc))) . " |\n";
  1470. }
  1471. } else {
  1472. die("Unexpected wikitype!"); # should have checked this elsewhere.
  1473. }
  1474. $sections{'Function Parameters'} = $str;
  1475. }
  1476. my $path = "$wikipath/$_.${wikitype}.tmp";
  1477. open(FH, '>', $path) or die("Can't open '$path': $!\n");
  1478. my $sectionsref = $wikisyms{$sym};
  1479. foreach (@standard_wiki_sections) {
  1480. # drop sections we either replaced or removed from the original wiki's contents.
  1481. if (not defined $only_wiki_sections{$_}) {
  1482. delete($$sectionsref{$_});
  1483. }
  1484. }
  1485. my $wikisectionorderref = $wikisectionorder{$sym};
  1486. # Make sure there's a footer in the wiki that puts this function in CategoryAPI...
  1487. if (not $$sectionsref{'[footer]'}) {
  1488. $$sectionsref{'[footer]'} = '';
  1489. push @$wikisectionorderref, '[footer]';
  1490. }
  1491. # If changing format, convert things that otherwise are passed through unmolested.
  1492. if (defined $changeformat) {
  1493. if (($dewikify_mode eq 'md') and ($origwikitype eq 'mediawiki')) {
  1494. $$sectionsref{'[footer]'} =~ s/\[\[(Category[a-zA-Z0-9_]+)\]\]/[$1]($1)/g;
  1495. } elsif (($dewikify_mode eq 'mediawiki') and ($origwikitype eq 'md')) {
  1496. $$sectionsref{'[footer]'} =~ s/\[(Category[a-zA-Z0-9_]+)\]\(.*?\)/[[$1]]/g;
  1497. }
  1498. foreach (keys %only_wiki_sections) {
  1499. my $sect = $_;
  1500. if (defined $$sectionsref{$sect}) {
  1501. $$sectionsref{$sect} = wikify($wikitype, dewikify($origwikitype, $$sectionsref{$sect}));
  1502. }
  1503. }
  1504. }
  1505. my $footer = $$sectionsref{'[footer]'};
  1506. my $symtypename;
  1507. if ($symtype == 1) {
  1508. $symtypename = 'Function';
  1509. } elsif ($symtype == 2) {
  1510. $symtypename = 'Macro';
  1511. } elsif ($symtype == 3) {
  1512. $symtypename = 'Struct';
  1513. } elsif ($symtype == 4) {
  1514. $symtypename = 'Enum';
  1515. } elsif ($symtype == 5) {
  1516. $symtypename = 'Datatype';
  1517. } else {
  1518. die("Unexpected symbol type $symtype!");
  1519. }
  1520. if ($wikitype eq 'mediawiki') {
  1521. $footer =~ s/\[\[CategoryAPI\]\],?\s*//g;
  1522. $footer =~ s/\[\[CategoryAPI${symtypename}\]\],?\s*//g;
  1523. $footer = "[[CategoryAPI]], [[CategoryAPI$symtypename]]" . (($footer eq '') ? "\n" : ", $footer");
  1524. } elsif ($wikitype eq 'md') {
  1525. $footer =~ s/\[CategoryAPI\]\(CategoryAPI\),?\s*//g;
  1526. $footer =~ s/\[CategoryAPI${symtypename}\]\(CategoryAPI${symtypename}\),?\s*//g;
  1527. $footer = "[CategoryAPI](CategoryAPI), [CategoryAPI$symtypename](CategoryAPI$symtypename)" . (($footer eq '') ? '' : ', ') . $footer;
  1528. } else { die("Unexpected wikitype '$wikitype'"); }
  1529. $$sectionsref{'[footer]'} = $footer;
  1530. if (defined $wikipreamble) {
  1531. my $wikified_preamble = wikify($wikitype, $wikipreamble);
  1532. if ($wikitype eq 'mediawiki') {
  1533. print FH "====== $wikified_preamble ======\n";
  1534. } elsif ($wikitype eq 'md') {
  1535. print FH "###### $wikified_preamble\n";
  1536. } else { die("Unexpected wikitype '$wikitype'"); }
  1537. }
  1538. my $prevsectstr = '';
  1539. my @ordered_sections = (@standard_wiki_sections, defined $wikisectionorderref ? @$wikisectionorderref : ()); # this copies the arrays into one.
  1540. foreach (@ordered_sections) {
  1541. my $sect = $_;
  1542. next if $sect eq '[start]';
  1543. next if (not defined $sections{$sect} and not defined $$sectionsref{$sect});
  1544. my $section = defined $sections{$sect} ? $sections{$sect} : $$sectionsref{$sect};
  1545. if ($sect eq '[footer]') {
  1546. # Make sure previous section ends with two newlines.
  1547. if (substr($prevsectstr, -1) ne "\n") {
  1548. print FH "\n\n";
  1549. } elsif (substr($prevsectstr, -2) ne "\n\n") {
  1550. print FH "\n";
  1551. }
  1552. print FH "----\n"; # It's the same in Markdown and MediaWiki.
  1553. } elsif ($sect eq '[Brief]') {
  1554. if ($wikitype eq 'mediawiki') {
  1555. print FH "= $sym =\n\n";
  1556. } elsif ($wikitype eq 'md') {
  1557. print FH "# $sym\n\n";
  1558. } else { die("Unexpected wikitype '$wikitype'"); }
  1559. } else {
  1560. my $sectname = $sect;
  1561. if ($sectname eq 'Function Parameters') { # We use this same table for different things depending on what we're documenting, so rename it now.
  1562. if (($symtype == 1) || ($symtype == 5)) { # function (or typedef, in case it's a function pointer type).
  1563. } elsif ($symtype == 2) { # macro
  1564. $sectname = 'Macro Parameters';
  1565. } elsif ($symtype == 3) { # struct/union
  1566. $sectname = 'Fields';
  1567. } elsif ($symtype == 4) { # enum
  1568. $sectname = 'Values';
  1569. } else {
  1570. die("Unexpected symtype $symtype");
  1571. }
  1572. }
  1573. if ($wikitype eq 'mediawiki') {
  1574. print FH "\n== $sectname ==\n\n";
  1575. } elsif ($wikitype eq 'md') {
  1576. print FH "\n## $sectname\n\n";
  1577. } else { die("Unexpected wikitype '$wikitype'"); }
  1578. }
  1579. my $sectstr = defined $sections{$sect} ? $sections{$sect} : $$sectionsref{$sect};
  1580. print FH $sectstr;
  1581. $prevsectstr = $sectstr;
  1582. # make sure these don't show up twice.
  1583. delete($sections{$sect});
  1584. delete($$sectionsref{$sect});
  1585. }
  1586. print FH "\n\n";
  1587. close(FH);
  1588. if (defined $changeformat and ($origwikitype ne $wikitype)) {
  1589. system("cd '$wikipath' ; git mv '$_.${origwikitype}' '$_.${wikitype}'");
  1590. unlink("$wikipath/$_.${origwikitype}");
  1591. }
  1592. rename($path, "$wikipath/$_.${wikitype}") or die("Can't rename '$path' to '$wikipath/$_.${wikitype}': $!\n");
  1593. }
  1594. # Write out simple redirector pages if they don't already exist.
  1595. foreach (keys %referenceonly) {
  1596. my $sym = $_;
  1597. my $refersto = $referenceonly{$sym};
  1598. my $path = "$wikipath/$sym.md"; # we only do Markdown for these.
  1599. next if (-f $path); # don't overwrite if it already exists. Delete the file if you need a rebuild!
  1600. open(FH, '>', $path) or die("Can't open '$path': $!\n");
  1601. if (defined $wikipreamble) {
  1602. my $wikified_preamble = wikify('md', $wikipreamble);
  1603. print FH "###### $wikified_preamble\n";
  1604. }
  1605. print FH "# $sym\n\nPlease refer to [$refersto]($refersto) for details.\n\n";
  1606. #print FH "----\n";
  1607. #print FH "[CategoryAPI](CategoryAPI)\n\n";
  1608. close(FH);
  1609. }
  1610. if (defined $readmepath) {
  1611. if ( -d $readmepath ) {
  1612. mkdir($wikireadmepath); # just in case
  1613. opendir(DH, $readmepath) or die("Can't opendir '$readmepath': $!\n");
  1614. while (my $d = readdir(DH)) {
  1615. my $dent = $d;
  1616. if ($dent =~ /\AREADME\-(.*?\.md)\Z/) { # we only bridge Markdown files here.
  1617. my $wikifname = $1;
  1618. next if $wikifname eq 'FrontPage.md';
  1619. filecopy("$readmepath/$dent", "$wikireadmepath/$wikifname", "\n");
  1620. }
  1621. }
  1622. closedir(DH);
  1623. my @pages = ();
  1624. opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n");
  1625. while (my $d = readdir(DH)) {
  1626. my $dent = $d;
  1627. if ($dent =~ /\A(.*?)\.(mediawiki|md)\Z/) {
  1628. my $wikiname = $1;
  1629. next if $wikiname eq 'FrontPage';
  1630. push @pages, $wikiname;
  1631. }
  1632. }
  1633. closedir(DH);
  1634. open(FH, '>', "$wikireadmepath/FrontPage.md") or die("Can't open '$wikireadmepath/FrontPage.md': $!\n");
  1635. print FH "# All READMEs available here\n\n";
  1636. foreach (sort @pages) {
  1637. my $wikiname = $_;
  1638. print FH "- [$wikiname]($wikiname)\n";
  1639. }
  1640. close(FH);
  1641. }
  1642. }
  1643. } elsif ($copy_direction == -2) { # --copy-to-manpages
  1644. # This only takes from the wiki data, since it has sections we omit from the headers, like code examples.
  1645. File::Path::make_path("$manpath/man3");
  1646. $dewikify_mode = 'manpage';
  1647. $wordwrap_mode = 'manpage';
  1648. my $introtxt = '';
  1649. if (0) {
  1650. open(FH, '<', "$srcpath/LICENSE.txt") or die("Can't open '$srcpath/LICENSE.txt': $!\n");
  1651. while (<FH>) {
  1652. chomp;
  1653. $introtxt .= ".\\\" $_\n";
  1654. }
  1655. close(FH);
  1656. }
  1657. if (!$gitrev) {
  1658. $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`;
  1659. chomp($gitrev);
  1660. }
  1661. # !!! FIXME
  1662. open(FH, '<', "$srcpath/$versionfname") or die("Can't open '$srcpath/$versionfname': $!\n");
  1663. my $majorver = 0;
  1664. my $minorver = 0;
  1665. my $patchver = 0;
  1666. while (<FH>) {
  1667. chomp;
  1668. if (/$versionmajorregex/) {
  1669. $majorver = int($1);
  1670. } elsif (/$versionminorregex/) {
  1671. $minorver = int($1);
  1672. } elsif (/$versionpatchregex/) {
  1673. $patchver = int($1);
  1674. }
  1675. }
  1676. close(FH);
  1677. my $fullversion = "$majorver.$minorver.$patchver";
  1678. foreach (keys %headersyms) {
  1679. my $sym = $_;
  1680. next if not defined $wikisyms{$sym}; # don't have a page for that function, skip it.
  1681. my $symtype = $headersymstype{$sym};
  1682. my $wikitype = $wikitypes{$sym};
  1683. my $sectionsref = $wikisyms{$sym};
  1684. my $remarks = $sectionsref->{'Remarks'};
  1685. my $params = $sectionsref->{'Function Parameters'};
  1686. my $returns = $sectionsref->{'Return Value'};
  1687. my $version = $sectionsref->{'Version'};
  1688. my $threadsafety = $sectionsref->{'Thread Safety'};
  1689. my $related = $sectionsref->{'See Also'};
  1690. my $examples = $sectionsref->{'Code Examples'};
  1691. my $deprecated = $sectionsref->{'Deprecated'};
  1692. my $headerfile = $manpageheaderfiletext;
  1693. $headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g;
  1694. $headerfile .= "\n";
  1695. my $mansection;
  1696. my $mansectionname;
  1697. if (($symtype == 1) || ($symtype == 2)) { # functions or macros
  1698. $mansection = '3';
  1699. $mansectionname = 'FUNCTIONS';
  1700. } elsif (($symtype >= 3) && ($symtype <= 5)) { # struct/union/enum/typedef
  1701. $mansection = '3type';
  1702. $mansectionname = 'DATATYPES';
  1703. } else {
  1704. die("Unexpected symtype $symtype");
  1705. }
  1706. my $brief = $sectionsref->{'[Brief]'};
  1707. my $decl = $headerdecls{$sym};
  1708. my $str = '';
  1709. $brief = "$brief";
  1710. $brief =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms;
  1711. $brief =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms;
  1712. $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  1713. my @briefsplit = split /\n/, $brief;
  1714. $brief = shift @briefsplit;
  1715. $brief = dewikify($wikitype, $brief);
  1716. if (defined $remarks) {
  1717. $remarks = dewikify($wikitype, join("\n", @briefsplit) . $remarks);
  1718. }
  1719. $str .= $introtxt;
  1720. $str .= ".\\\" This manpage content is licensed under Creative Commons\n";
  1721. $str .= ".\\\" Attribution 4.0 International (CC BY 4.0)\n";
  1722. $str .= ".\\\" https://creativecommons.org/licenses/by/4.0/\n";
  1723. $str .= ".\\\" This manpage was generated from ${projectshortname}'s wiki page for $sym:\n";
  1724. $str .= ".\\\" $wikiurl/$sym\n";
  1725. $str .= ".\\\" Generated with SDL/build-scripts/wikiheaders.pl\n";
  1726. $str .= ".\\\" revision $gitrev\n" if $gitrev ne '';
  1727. $str .= ".\\\" Please report issues in this manpage's content at:\n";
  1728. $str .= ".\\\" $bugreporturl\n";
  1729. $str .= ".\\\" Please report issues in the generation of this manpage from the wiki at:\n";
  1730. $str .= ".\\\" https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n";
  1731. $str .= ".\\\" $projectshortname can be found at $projecturl\n";
  1732. # 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.
  1733. # This wizadry is from https://web.archive.org/web/20060102165607/http://people.debian.org/~branden/talks/wtfm/wtfm.pdf
  1734. $str .= ".de URL\n";
  1735. $str .= '\\$2 \(laURL: \\$1 \(ra\\$3' . "\n";
  1736. $str .= "..\n";
  1737. $str .= '.if \n[.g] .mso www.tmac' . "\n";
  1738. $str .= ".TH $sym $mansection \"$projectshortname $fullversion\" \"$projectfullname\" \"$projectshortname$majorver $mansectionname\"\n";
  1739. $str .= ".SH NAME\n";
  1740. $str .= "$sym";
  1741. $str .= " \\- $brief" if (defined $brief);
  1742. $str .= "\n";
  1743. if (defined $deprecated) {
  1744. $str .= ".SH DEPRECATED\n";
  1745. $str .= dewikify($wikitype, $deprecated) . "\n";
  1746. }
  1747. if (defined $headerfile) {
  1748. $str .= ".SH HEADER FILE\n";
  1749. $str .= dewikify($wikitype, $headerfile) . "\n";
  1750. }
  1751. $str .= ".SH SYNOPSIS\n";
  1752. $str .= ".nf\n";
  1753. $str .= ".B #include \\(dq$mainincludefname\\(dq\n";
  1754. $str .= ".PP\n";
  1755. my @decllines = split /\n/, $decl;
  1756. foreach (@decllines) {
  1757. $str .= ".BI \"$_\n";
  1758. }
  1759. $str .= ".fi\n";
  1760. if (defined $remarks) {
  1761. $str .= ".SH DESCRIPTION\n";
  1762. $str .= $remarks . "\n";
  1763. }
  1764. if (defined $params) {
  1765. if (($symtype == 1) || ($symtype == 5)) {
  1766. $str .= ".SH FUNCTION PARAMETERS\n";
  1767. } elsif ($symtype == 2) { # macro
  1768. $str .= ".SH MACRO PARAMETERS\n";
  1769. } elsif ($symtype == 3) { # struct/union
  1770. $str .= ".SH FIELDS\n";
  1771. } elsif ($symtype == 4) { # enum
  1772. $str .= ".SH VALUES\n";
  1773. } else {
  1774. die("Unexpected symtype $symtype");
  1775. }
  1776. my @lines = split /\n/, $params;
  1777. if ($wikitype eq 'mediawiki') {
  1778. die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start
  1779. while (scalar(@lines) >= 3) {
  1780. my $name = shift @lines;
  1781. my $desc = shift @lines;
  1782. my $terminator = shift @lines; # the '|-' or '|}' line.
  1783. last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table.
  1784. $name =~ s/\A\|\s*//;
  1785. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  1786. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  1787. $desc =~ s/\A\|\s*//;
  1788. $desc = dewikify($wikitype, $desc);
  1789. #print STDERR "FN: $sym NAME: $name DESC: $desc TERM: $terminator\n";
  1790. $str .= ".TP\n";
  1791. $str .= ".I $name\n";
  1792. $str .= "$desc\n";
  1793. }
  1794. } elsif ($wikitype eq 'md') {
  1795. my $l;
  1796. $l = shift @lines;
  1797. die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*\|\s*\|\s*\|\s*\Z/);
  1798. $l = shift @lines;
  1799. die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/);
  1800. while (scalar(@lines) >= 1) {
  1801. $l = shift @lines;
  1802. if ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) {
  1803. my $name = $1;
  1804. my $desc = $2;
  1805. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  1806. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  1807. $desc = dewikify($wikitype, $desc);
  1808. $str .= ".TP\n";
  1809. $str .= ".I $name\n";
  1810. $str .= "$desc\n";
  1811. } else {
  1812. last; # we seem to have run out of table.
  1813. }
  1814. }
  1815. } else {
  1816. die("write me");
  1817. }
  1818. }
  1819. if (defined $returns) {
  1820. $str .= ".SH RETURN VALUE\n";
  1821. $str .= dewikify($wikitype, $returns) . "\n";
  1822. }
  1823. if (defined $examples) {
  1824. $str .= ".SH CODE EXAMPLES\n";
  1825. $dewikify_manpage_code_indent = 0;
  1826. $str .= dewikify($wikitype, $examples) . "\n";
  1827. $dewikify_manpage_code_indent = 1;
  1828. }
  1829. if (defined $threadsafety) {
  1830. $str .= ".SH THREAD SAFETY\n";
  1831. $str .= dewikify($wikitype, $threadsafety) . "\n";
  1832. }
  1833. if (defined $version) {
  1834. $str .= ".SH AVAILABILITY\n";
  1835. $str .= dewikify($wikitype, $version) . "\n";
  1836. }
  1837. if (defined $related) {
  1838. $str .= ".SH SEE ALSO\n";
  1839. # !!! FIXME: lots of code duplication in all of these.
  1840. my $v = dewikify($wikitype, $related);
  1841. my @desclines = split /\n/, $v;
  1842. my $nextstr = '';
  1843. foreach (@desclines) {
  1844. s/\A(\:|\* )//;
  1845. s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func"
  1846. s/\[\[(.*?)\]\]/$1/; # in case some wikilinks remain.
  1847. s/\[(.*?)\]\(.*?\)/$1/; # in case some wikilinks remain.
  1848. s/\A\*\s*\Z//;
  1849. s/\A\/*//;
  1850. s/\A\.BR\s+//; # dewikify added this, but we want to handle it.
  1851. s/\A\.I\s+//; # dewikify added this, but we want to handle it.
  1852. s/\A\s+//;
  1853. s/\s+\Z//;
  1854. next if $_ eq '';
  1855. my $seealso_symtype = $headersymstype{$_};
  1856. my $seealso_mansection = '3';
  1857. if (defined($seealso_symtype) && ($seealso_symtype >= 3) && ($seealso_symtype <= 5)) { # struct/union/enum/typedef
  1858. $seealso_mansection = '3type';
  1859. }
  1860. $str .= "$nextstr.BR $_ ($seealso_mansection)";
  1861. $nextstr = ",\n";
  1862. }
  1863. $str .= "\n";
  1864. }
  1865. if (0) {
  1866. $str .= ".SH COPYRIGHT\n";
  1867. $str .= "This manpage is licensed under\n";
  1868. $str .= ".UR https://creativecommons.org/licenses/by/4.0/\n";
  1869. $str .= "Creative Commons Attribution 4.0 International (CC BY 4.0)\n";
  1870. $str .= ".UE\n";
  1871. $str .= ".PP\n";
  1872. $str .= "This manpage was generated from\n";
  1873. $str .= ".UR $wikiurl/$sym\n";
  1874. $str .= "${projectshortname}'s wiki\n";
  1875. $str .= ".UE\n";
  1876. $str .= "using SDL/build-scripts/wikiheaders.pl";
  1877. $str .= " revision $gitrev" if $gitrev ne '';
  1878. $str .= ".\n";
  1879. $str .= "Please report issues in this manpage at\n";
  1880. $str .= ".UR $bugreporturl\n";
  1881. $str .= "our bugtracker!\n";
  1882. $str .= ".UE\n";
  1883. }
  1884. my $path = "$manpath/man3/$_.$mansection";
  1885. my $tmppath = "$path.tmp";
  1886. open(FH, '>', $tmppath) or die("Can't open '$tmppath': $!\n");
  1887. print FH $str;
  1888. close(FH);
  1889. rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n");
  1890. }
  1891. } elsif ($copy_direction == -3) { # --report-coverage_gaps
  1892. foreach (@coverage_gap) {
  1893. print("$_\n");
  1894. }
  1895. }
  1896. # end of wikiheaders.pl ...