wikiheaders.pl 116 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969
  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 $versionmicroregex = '\A\#define\s+SDL_MICRO_VERSION\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 $headercategoryeval = undef;
  32. my $changeformat = undef;
  33. my $manpath = undef;
  34. my $gitrev = undef;
  35. foreach (@ARGV) {
  36. $warn_about_missing = 1, next if $_ eq '--warn-about-missing';
  37. $copy_direction = 1, next if $_ eq '--copy-to-headers';
  38. $copy_direction = 1, next if $_ eq '--copy-to-header';
  39. $copy_direction = -1, next if $_ eq '--copy-to-wiki';
  40. $copy_direction = -2, next if $_ eq '--copy-to-manpages';
  41. $copy_direction = -3, next if $_ eq '--report-coverage-gaps';
  42. $copy_direction = -4, next if $_ eq '--copy-to-latex';
  43. if (/\A--options=(.*)\Z/) {
  44. $optionsfname = $1;
  45. next;
  46. } elsif (/\A--changeformat=(.*)\Z/) {
  47. $changeformat = $1;
  48. next;
  49. } elsif (/\A--manpath=(.*)\Z/) {
  50. $manpath = $1;
  51. next;
  52. } elsif (/\A--rev=(.*)\Z/) {
  53. $gitrev = $1;
  54. next;
  55. }
  56. $srcpath = $_, next if not defined $srcpath;
  57. $wikipath = $_, next if not defined $wikipath;
  58. }
  59. my $default_optionsfname = '.wikiheaders-options';
  60. $default_optionsfname = "$srcpath/$default_optionsfname" if defined $srcpath;
  61. if ((not defined $optionsfname) && (-f $default_optionsfname)) {
  62. $optionsfname = $default_optionsfname;
  63. }
  64. if (defined $optionsfname) {
  65. open OPTIONS, '<', $optionsfname or die("Failed to open options file '$optionsfname': $!\n");
  66. while (<OPTIONS>) {
  67. next if /\A\s*\#/; # Skip lines that start with (optional whitespace, then) '#' as comments.
  68. chomp;
  69. if (/\A(.*?)\=(.*)\Z/) {
  70. my $key = $1;
  71. my $val = $2;
  72. $key =~ s/\A\s+//;
  73. $key =~ s/\s+\Z//;
  74. $val =~ s/\A\s+//;
  75. $val =~ s/\s+\Z//;
  76. $warn_about_missing = int($val), next if $key eq 'warn_about_missing';
  77. $srcpath = $val, next if $key eq 'srcpath';
  78. $wikipath = $val, next if $key eq 'wikipath';
  79. $apiprefixregex = $val, next if $key eq 'apiprefixregex';
  80. $projectfullname = $val, next if $key eq 'projectfullname';
  81. $projectshortname = $val, next if $key eq 'projectshortname';
  82. $wikisubdir = $val, next if $key eq 'wikisubdir';
  83. $incsubdir = $val, next if $key eq 'incsubdir';
  84. $readmesubdir = $val, next if $key eq 'readmesubdir';
  85. $versionmajorregex = $val, next if $key eq 'versionmajorregex';
  86. $versionminorregex = $val, next if $key eq 'versionminorregex';
  87. $versionmicroregex = $val, next if $key eq 'versionmicroregex';
  88. $versionfname = $val, next if $key eq 'versionfname';
  89. $mainincludefname = $val, next if $key eq 'mainincludefname';
  90. $selectheaderregex = $val, next if $key eq 'selectheaderregex';
  91. $projecturl = $val, next if $key eq 'projecturl';
  92. $wikiurl = $val, next if $key eq 'wikiurl';
  93. $bugreporturl = $val, next if $key eq 'bugreporturl';
  94. $wikipreamble = $val, next if $key eq 'wikipreamble';
  95. $wikiheaderfiletext = $val, next if $key eq 'wikiheaderfiletext';
  96. $manpageheaderfiletext = $val, next if $key eq 'manpageheaderfiletext';
  97. $headercategoryeval = $val, next if $key eq 'headercategoryeval';
  98. }
  99. }
  100. close(OPTIONS);
  101. }
  102. sub escLaTeX {
  103. my $str = shift;
  104. $str =~ s/([_\#\&\^])/\\$1/g;
  105. return $str;
  106. }
  107. my $wordwrap_mode = 'mediawiki';
  108. sub wordwrap_atom { # don't call this directly.
  109. my $str = shift;
  110. my $retval = '';
  111. # wordwrap but leave links intact, even if they overflow.
  112. if ($wordwrap_mode eq 'mediawiki') {
  113. while ($str =~ s/(.*?)\s*(\[https?\:\/\/.*?\s+.*?\])\s*//ms) {
  114. $retval .= fill('', '', $1); # wrap it.
  115. $retval .= "\n$2\n"; # don't wrap it.
  116. }
  117. } elsif ($wordwrap_mode eq 'md') {
  118. while ($str =~ s/(.*?)\s*(\[.*?\]\(https?\:\/\/.*?\))\s*//ms) {
  119. $retval .= fill('', '', $1); # wrap it.
  120. $retval .= "\n$2\n"; # don't wrap it.
  121. }
  122. }
  123. return $retval . fill('', '', $str);
  124. }
  125. sub wordwrap_with_bullet_indent { # don't call this directly.
  126. my $bullet = shift;
  127. my $str = shift;
  128. my $retval = '';
  129. #print("WORDWRAP BULLET ('$bullet'):\n\n$str\n\n");
  130. # You _can't_ (at least with Pandoc) have a bullet item with a newline in
  131. # MediaWiki, so _remove_ wrapping!
  132. if ($wordwrap_mode eq 'mediawiki') {
  133. $retval = "$bullet$str";
  134. $retval =~ s/\n/ /gms;
  135. $retval =~ s/\s+$//gms;
  136. #print("WORDWRAP BULLET DONE:\n\n$retval\n\n");
  137. return "$retval\n";
  138. }
  139. my $bulletlen = length($bullet);
  140. # wrap it and then indent each line to be under the bullet.
  141. $Text::Wrap::columns -= $bulletlen;
  142. my @wrappedlines = split /\n/, wordwrap_atom($str);
  143. $Text::Wrap::columns += $bulletlen;
  144. my $prefix = $bullet;
  145. my $usual_prefix = ' ' x $bulletlen;
  146. foreach (@wrappedlines) {
  147. s/\s*\Z//;
  148. $retval .= "$prefix$_\n";
  149. $prefix = $usual_prefix;
  150. }
  151. return $retval;
  152. }
  153. sub wordwrap_one_paragraph { # don't call this directly.
  154. my $retval = '';
  155. my $p = shift;
  156. #print "\n\n\nPARAGRAPH: [$p]\n\n\n";
  157. if ($p =~ s/\A([\*\-] )//) { # bullet list, starts with "* " or "- ".
  158. my $bullet = $1;
  159. my $item = '';
  160. my @items = split /\n/, $p;
  161. foreach (@items) {
  162. if (s/\A([\*\-] )//) {
  163. $retval .= wordwrap_with_bullet_indent($bullet, $item);
  164. $item = '';
  165. }
  166. s/\A\s*//;
  167. $item .= "$_\n"; # accumulate lines until we hit the end or another bullet.
  168. }
  169. if ($item ne '') {
  170. $retval .= wordwrap_with_bullet_indent($bullet, $item);
  171. }
  172. } elsif ($p =~ /\A\s*\|.*\|\s*\n/) { # Markdown table
  173. $retval = "$p\n"; # don't wrap it (!!! FIXME: but maybe parse by lines until we run out of table...)
  174. } else {
  175. $retval = wordwrap_atom($p) . "\n";
  176. }
  177. return $retval;
  178. }
  179. sub wordwrap_paragraphs { # don't call this directly.
  180. my $str = shift;
  181. my $retval = '';
  182. my @paragraphs = split /\n\n/, $str;
  183. foreach (@paragraphs) {
  184. next if $_ eq '';
  185. $retval .= wordwrap_one_paragraph($_);
  186. $retval .= "\n";
  187. }
  188. return $retval;
  189. }
  190. my $wordwrap_default_columns = 76;
  191. sub wordwrap {
  192. my $str = shift;
  193. my $columns = shift;
  194. $columns = $wordwrap_default_columns if not defined $columns;
  195. $columns += $wordwrap_default_columns if $columns < 0;
  196. $Text::Wrap::columns = $columns;
  197. my $retval = '';
  198. #print("\n\nWORDWRAP:\n\n$str\n\n\n");
  199. $str =~ s/\A\n+//ms;
  200. while ($str =~ s/(.*?)(\`\`\`.*?\`\`\`|\<syntaxhighlight.*?\<\/syntaxhighlight\>)//ms) {
  201. #print("\n\nWORDWRAP BLOCK:\n\n$1\n\n ===\n\n$2\n\n\n");
  202. $retval .= wordwrap_paragraphs($1); # wrap it.
  203. $retval .= "$2\n\n"; # don't wrap it.
  204. }
  205. $retval .= wordwrap_paragraphs($str); # wrap what's left.
  206. $retval =~ s/\n+\Z//ms;
  207. #print("\n\nWORDWRAP DONE:\n\n$retval\n\n\n");
  208. return $retval;
  209. }
  210. # This assumes you're moving from Markdown (in the Doxygen data) to Wiki, which
  211. # is why the 'md' section is so sparse.
  212. sub wikify_chunk {
  213. my $wikitype = shift;
  214. my $str = shift;
  215. my $codelang = shift;
  216. my $code = shift;
  217. #print("\n\nWIKIFY CHUNK:\n\n$str\n\n\n");
  218. if ($wikitype eq 'mediawiki') {
  219. # convert `code` things first, so they aren't mistaken for other markdown items.
  220. my $codedstr = '';
  221. while ($str =~ s/\A(.*?)\`(.*?)\`//ms) {
  222. my $codeblock = $2;
  223. $codedstr .= wikify_chunk($wikitype, $1, undef, undef);
  224. if (defined $apiprefixregex) {
  225. # Convert obvious API things to wikilinks, even inside `code` blocks.
  226. $codeblock =~ s/\b($apiprefixregex[a-zA-Z0-9_]+)/[[$1]]/gms;
  227. }
  228. $codedstr .= "<code>$codeblock</code>";
  229. }
  230. # Convert obvious API things to wikilinks.
  231. if (defined $apiprefixregex) {
  232. $str =~ s/\b($apiprefixregex[a-zA-Z0-9_]+)/[[$1]]/gms;
  233. }
  234. # Make some Markdown things into MediaWiki...
  235. # links
  236. $str =~ s/\[(.*?)\]\((https?\:\/\/.*?)\)/\[$2 $1\]/g;
  237. # bold+italic
  238. $str =~ s/\*\*\*(.*?)\*\*\*/'''''$1'''''/gms;
  239. # bold
  240. $str =~ s/\*\*(.*?)\*\*/'''$1'''/gms;
  241. # italic
  242. $str =~ s/\*(.*?)\*/''$1''/gms;
  243. # bullets
  244. $str =~ s/^\- /* /gm;
  245. $str = $codedstr . $str;
  246. if (defined $code) {
  247. $str .= "<syntaxhighlight lang='$codelang'>$code<\/syntaxhighlight>";
  248. }
  249. } elsif ($wikitype eq 'md') {
  250. # convert `code` things first, so they aren't mistaken for other markdown items.
  251. my $codedstr = '';
  252. while ($str =~ s/\A(.*?)(\`.*?\`)//ms) {
  253. my $codeblock = $2;
  254. $codedstr .= wikify_chunk($wikitype, $1, undef, undef);
  255. if (defined $apiprefixregex) {
  256. # Convert obvious API things to wikilinks, even inside `code` blocks,
  257. # BUT ONLY IF the entire code block is the API thing,
  258. # So something like "just call `SDL_Whatever`" will become
  259. # "just call [`SDL_Whatever`](SDL_Whatever)", but
  260. # "just call `SDL_Whatever(7)`" will not. It's just the safest
  261. # way to do this without resorting to wrapping things in html <code> tags.
  262. $codeblock =~ s/\A\`($apiprefixregex[a-zA-Z0-9_]+)\`\Z/[`$1`]($1)/gms;
  263. }
  264. $codedstr .= $codeblock;
  265. }
  266. # Convert obvious API things to wikilinks.
  267. if (defined $apiprefixregex) {
  268. $str =~ s/\b($apiprefixregex[a-zA-Z0-9_]+)/[$1]($1)/gms;
  269. }
  270. $str = $codedstr . $str;
  271. if (defined $code) {
  272. $str .= "```$codelang\n$code\n```\n";
  273. }
  274. }
  275. #print("\n\nWIKIFY CHUNK DONE:\n\n$str\n\n\n");
  276. return $str;
  277. }
  278. sub wikify {
  279. my $wikitype = shift;
  280. my $str = shift;
  281. my $retval = '';
  282. #print("WIKIFY WHOLE:\n\n$str\n\n\n");
  283. while ($str =~ s/\A(.*?)\`\`\`(.*?)\n(.*?)\n\`\`\`(\n|\Z)//ms) {
  284. $retval .= wikify_chunk($wikitype, $1, $2, $3);
  285. }
  286. $retval .= wikify_chunk($wikitype, $str, undef, undef);
  287. #print("WIKIFY WHOLE DONE:\n\n$retval\n\n\n");
  288. return $retval;
  289. }
  290. my $dewikify_mode = 'md';
  291. my $dewikify_manpage_code_indent = 1;
  292. sub dewikify_chunk {
  293. my $wikitype = shift;
  294. my $str = shift;
  295. my $codelang = shift;
  296. my $code = shift;
  297. #print("\n\nDEWIKIFY CHUNK:\n\n$str\n\n\n");
  298. if ($dewikify_mode eq 'md') {
  299. if ($wikitype eq 'mediawiki') {
  300. # Doxygen supports Markdown (and it just simply looks better than MediaWiki
  301. # when looking at the raw headers), so do some conversions here as necessary.
  302. # Dump obvious wikilinks.
  303. if (defined $apiprefixregex) {
  304. $str =~ s/\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]/$1/gms;
  305. }
  306. # links
  307. $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\[$2\]\($1\)/g;
  308. # <code></code> is also popular. :/
  309. $str =~ s/\<code>(.*?)<\/code>/`$1`/gms;
  310. # bold+italic
  311. $str =~ s/'''''(.*?)'''''/***$1***/gms;
  312. # bold
  313. $str =~ s/'''(.*?)'''/**$1**/gms;
  314. # italic
  315. $str =~ s/''(.*?)''/*$1*/gms;
  316. # bullets
  317. $str =~ s/^\* /- /gm;
  318. } elsif ($wikitype eq 'md') {
  319. # Dump obvious wikilinks. The rest can just passthrough.
  320. if (defined $apiprefixregex) {
  321. $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/$1/gms;
  322. }
  323. }
  324. if (defined $code) {
  325. $str .= "\n```$codelang\n$code\n```\n";
  326. }
  327. } elsif ($dewikify_mode eq 'manpage') {
  328. $str =~ s/\./\\[char46]/gms; # make sure these can't become control codes.
  329. if ($wikitype eq 'mediawiki') {
  330. # Dump obvious wikilinks.
  331. if (defined $apiprefixregex) {
  332. $str =~ s/\s*\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]\s*/\n.BR $1\n/gms;
  333. }
  334. # links
  335. $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\n.URL "$1" "$2"\n/g;
  336. # <code></code> is also popular. :/
  337. $str =~ s/\s*\<code>(.*?)<\/code>\s*/\n.BR $1\n/gms;
  338. # bold+italic (this looks bad, just make it bold).
  339. $str =~ s/\s*'''''(.*?)'''''\s*/\n.B $1\n/gms;
  340. # bold
  341. $str =~ s/\s*'''(.*?)'''\s*/\n.B $1\n/gms;
  342. # italic
  343. $str =~ s/\s*''(.*?)''\s*/\n.I $1\n/gms;
  344. # bullets
  345. $str =~ s/^\* /\n\\\(bu /gm;
  346. } elsif ($wikitype eq 'md') {
  347. # Dump obvious wikilinks.
  348. if (defined $apiprefixregex) {
  349. $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/\n.BR $1\n/gms;
  350. }
  351. # links
  352. $str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\n.URL "$2" "$1"\n/g;
  353. # <code></code> is also popular. :/
  354. $str =~ s/\s*\`(.*?)\`\s*/\n.BR $1\n/gms;
  355. # bold+italic (this looks bad, just make it bold).
  356. $str =~ s/\s*\*\*\*(.*?)\*\*\*\s*/\n.B $1\n/gms;
  357. # bold
  358. $str =~ s/\s*\*\*(.*?)\*\*\s*/\n.B $1\n/gms;
  359. # italic
  360. $str =~ s/\s*\*(.*?)\*\s*/\n.I $1\n/gms;
  361. # bullets
  362. $str =~ s/^\- /\n\\\(bu /gm;
  363. }
  364. if (defined $code) {
  365. $code =~ s/\A\n+//gms;
  366. $code =~ s/\n+\Z//gms;
  367. if ($dewikify_manpage_code_indent) {
  368. $str .= "\n.IP\n"
  369. } else {
  370. $str .= "\n.PP\n"
  371. }
  372. $str .= ".EX\n$code\n.EE\n.PP\n";
  373. }
  374. } elsif ($dewikify_mode eq 'LaTeX') {
  375. if ($wikitype eq 'mediawiki') {
  376. # Dump obvious wikilinks.
  377. if (defined $apiprefixregex) {
  378. $str =~ s/\s*\[\[($apiprefixregex[a-zA-Z0-9_]+)\]\]/$1/gms;
  379. }
  380. # links
  381. $str =~ s/\[(https?\:\/\/.*?)\s+(.*?)\]/\\href{$1}{$2}/g;
  382. # <code></code> is also popular. :/
  383. $str =~ s/\s*\<code>(.*?)<\/code>/ \\texttt{$1}/gms;
  384. # bold+italic
  385. $str =~ s/\s*'''''(.*?)'''''/ \\textbf{\\textit{$1}}/gms;
  386. # bold
  387. $str =~ s/\s*'''(.*?)'''/ \\textbf{$1}/gms;
  388. # italic
  389. $str =~ s/\s*''(.*?)''/ \\textit{$1}/gms;
  390. # bullets
  391. $str =~ s/^\*\s+/ \\item /gm;
  392. } elsif ($wikitype eq 'md') {
  393. # Dump obvious wikilinks.
  394. if (defined $apiprefixregex) {
  395. $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/$1/gms;
  396. }
  397. # links
  398. $str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\\href{$2}{$1}/g;
  399. # <code></code> is also popular. :/
  400. $str =~ s/\s*\`(.*?)\`/ \\texttt{$1}/gms;
  401. # bold+italic
  402. $str =~ s/\s*\*\*\*(.*?)\*\*\*/ \\textbf{\\textit{$1}}/gms;
  403. # bold
  404. $str =~ s/\s*\*\*(.*?)\*\*/ \\textbf{$1}/gms;
  405. # italic
  406. $str =~ s/\s*\*(.*?)\*/ \\textit{$1}/gms;
  407. # bullets
  408. $str =~ s/^\-\s+/ \\item /gm;
  409. }
  410. # Wrap bullet lists in itemize blocks...
  411. $str =~ s/^(\s*\\item .*?)(\n\n|\Z)/\n\\begin{itemize}\n$1$2\n\\end{itemize}\n\n/gms;
  412. $str = escLaTeX($str);
  413. if (defined $code) {
  414. $code =~ s/\A\n+//gms;
  415. $code =~ s/\n+\Z//gms;
  416. if (($codelang eq '') || ($codelang eq 'output')) {
  417. $str .= "\\begin{verbatim}\n$code\n\\end{verbatim}\n";
  418. } else {
  419. if ($codelang eq 'c') {
  420. $codelang = 'C';
  421. } elsif ($codelang eq 'c++') {
  422. $codelang = 'C++';
  423. } else {
  424. die("Unexpected codelang '$codelang'");
  425. }
  426. $str .= "\n\\lstset{language=$codelang}\n";
  427. $str .= "\\begin{lstlisting}\n$code\n\\end{lstlisting}\n";
  428. }
  429. }
  430. } else {
  431. die("Unexpected dewikify_mode");
  432. }
  433. #print("\n\nDEWIKIFY CHUNK DONE:\n\n$str\n\n\n");
  434. return $str;
  435. }
  436. sub dewikify {
  437. my $wikitype = shift;
  438. my $str = shift;
  439. return '' if not defined $str;
  440. #print("DEWIKIFY WHOLE:\n\n$str\n\n\n");
  441. $str =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms;
  442. $str =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms;
  443. my $retval = '';
  444. if ($wikitype eq 'mediawiki') {
  445. while ($str =~ s/\A(.*?)<syntaxhighlight lang='?(.*?)'?>(.*?)<\/syntaxhighlight\>//ms) {
  446. $retval .= dewikify_chunk($wikitype, $1, $2, $3);
  447. }
  448. } elsif ($wikitype eq 'md') {
  449. while ($str =~ s/\A(.*?)\n```(.*?)\n(.*?)\n```\n//ms) {
  450. $retval .= dewikify_chunk($wikitype, $1, $2, $3);
  451. }
  452. }
  453. $retval .= dewikify_chunk($wikitype, $str, undef, undef);
  454. #print("DEWIKIFY WHOLE DONE:\n\n$retval\n\n\n");
  455. return $retval;
  456. }
  457. sub filecopy {
  458. my $src = shift;
  459. my $dst = shift;
  460. my $endline = shift;
  461. $endline = "\n" if not defined $endline;
  462. open(COPYIN, '<', $src) or die("Failed to open '$src' for reading: $!\n");
  463. open(COPYOUT, '>', $dst) or die("Failed to open '$dst' for writing: $!\n");
  464. while (<COPYIN>) {
  465. chomp;
  466. s/[ \t\r\n]*\Z//;
  467. print COPYOUT "$_$endline";
  468. }
  469. close(COPYOUT);
  470. close(COPYIN);
  471. }
  472. sub usage {
  473. 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");
  474. }
  475. usage() if not defined $srcpath;
  476. usage() if not defined $wikipath;
  477. #usage() if $copy_direction == 0;
  478. if (not defined $manpath) {
  479. $manpath = "$srcpath/man";
  480. }
  481. my @standard_wiki_sections = (
  482. 'Draft',
  483. '[Brief]',
  484. 'Deprecated',
  485. 'Header File',
  486. 'Syntax',
  487. 'Function Parameters',
  488. 'Macro Parameters',
  489. 'Fields',
  490. 'Values',
  491. 'Return Value',
  492. 'Remarks',
  493. 'Thread Safety',
  494. 'Version',
  495. 'Code Examples',
  496. 'See Also'
  497. );
  498. # Sections that only ever exist in the wiki and shouldn't be deleted when
  499. # not found in the headers.
  500. my %only_wiki_sections = ( # The ones don't mean anything, I just need to check for key existence.
  501. 'Draft', 1,
  502. 'Code Examples', 1,
  503. 'Header File', 1
  504. );
  505. my %headers = (); # $headers{"SDL_audio.h"} -> reference to an array of all lines of text in SDL_audio.h.
  506. my %headersyms = (); # $headersyms{"SDL_OpenAudio"} -> string of header documentation for SDL_OpenAudio, with comment '*' bits stripped from the start. Newlines embedded!
  507. my %headerdecls = ();
  508. my %headersymslocation = (); # $headersymslocation{"SDL_OpenAudio"} -> name of header holding SDL_OpenAudio define ("SDL_audio.h" in this case).
  509. my %headersymschunk = (); # $headersymschunk{"SDL_OpenAudio"} -> offset in array in %headers that should be replaced for this symbol.
  510. my %headersymshasdoxygen = (); # $headersymshasdoxygen{"SDL_OpenAudio"} -> 1 if there was no existing doxygen for this function.
  511. my %headersymstype = (); # $headersymstype{"SDL_OpenAudio"} -> 1 (function), 2 (macro), 3 (struct), 4 (enum), 5 (other typedef)
  512. my %headersymscategory = (); # $headersymscategory{"SDL_OpenAudio"} -> 'Audio' ... this is set with a `/* WIKI CATEGEORY: Audio */` comment in the headers that sets it on all symbols until a new comment changes it. So usually, once at the top of the header file.
  513. my %headercategorydocs = (); # $headercategorydocs{"Audio"} -> (fake) symbol for this category's documentation. Undefined if not documented.
  514. my %headersymsparaminfo = (); # $headersymsparaminfo{"SDL_OpenAudio"} -> reference to array of parameters, pushed by name, then C type string, repeating. Undef'd if void params, or not a function.
  515. my %headersymsrettype = (); # $headersymsrettype{"SDL_OpenAudio"} -> string of C datatype of return value. Undef'd if not a function.
  516. my %wikitypes = (); # contains string of wiki page extension, like $wikitypes{"SDL_OpenAudio"} == 'mediawiki'
  517. 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"}.
  518. 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'
  519. 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.
  520. 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!
  521. sub add_coverage_gap {
  522. if ($copy_direction == -3) { # --report-coverage-gaps
  523. my $text = shift;
  524. my $dent = shift;
  525. my $lineno = shift;
  526. return if $text =~ /\A\s*\Z/; # skip blank lines
  527. return if $text =~ /\A\s*\#\s*(if|el|endif|include)/; # skip preprocessor floof.
  528. push @coverage_gap, "$dent:$lineno: $text";
  529. }
  530. }
  531. sub print_undocumented_section {
  532. my $fh = shift;
  533. my $typestr = shift;
  534. my $typeval = shift;
  535. print $fh "## $typestr defined in the headers, but not in the wiki\n\n";
  536. my $header_only_sym = 0;
  537. foreach (sort keys %headersyms) {
  538. my $sym = $_;
  539. if ((not defined $wikisyms{$sym}) && ($headersymstype{$sym} == $typeval)) {
  540. print $fh "- [$sym]($sym)\n";
  541. $header_only_sym = 1;
  542. }
  543. }
  544. if (!$header_only_sym) {
  545. print $fh "(none)\n";
  546. }
  547. print $fh "\n";
  548. 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?)
  549. print $fh "## $typestr defined in the wiki, but not in the headers\n\n";
  550. my $wiki_only_sym = 0;
  551. foreach (sort keys %wikisyms) {
  552. my $sym = $_;
  553. if ((not defined $headersyms{$sym}) && ($headersymstype{$sym} == $typeval)) {
  554. print $fh "- [$sym]($sym)\n";
  555. $wiki_only_sym = 1;
  556. }
  557. }
  558. if (!$wiki_only_sym) {
  559. print $fh "(none)\n";
  560. }
  561. print $fh "\n";
  562. }
  563. }
  564. sub strip_fn_declaration_metadata {
  565. my $decl = shift;
  566. $decl =~ s/SDL_(PRINTF|SCANF)_FORMAT_STRING\s*//; # don't want this metadata as part of the documentation.
  567. $decl =~ s/SDL_ALLOC_SIZE2?\(.*?\)\s*//; # don't want this metadata as part of the documentation.
  568. $decl =~ s/SDL_MALLOC\s*//; # don't want this metadata as part of the documentation.
  569. $decl =~ s/SDL_(IN|OUT|INOUT)_.*?CAP\s*\(.*?\)\s*//g; # don't want this metadata as part of the documentation.
  570. $decl =~ s/\)(\s*SDL_[a-zA-Z_]+(\(.*?\)|))*;/);/; # don't want this metadata as part of the documentation.
  571. return $decl;
  572. }
  573. sub sanitize_c_typename {
  574. my $str = shift;
  575. $str =~ s/\A\s+//;
  576. $str =~ s/\s+\Z//;
  577. $str =~ s/const\s*(\*+)/const $1/g; # one space between `const` and pointer stars: `char const* const *` becomes `char const * const *`.
  578. $str =~ s/\*\s+\*/**/g; # drop spaces between pointers: `void * *` becomes `void **`.
  579. $str =~ s/\s*(\*+)\Z/ $1/; # one space between pointer stars and what it points to: `void**` becomes `void **`.
  580. return $str;
  581. }
  582. my $incpath = "$srcpath";
  583. $incpath .= "/$incsubdir" if $incsubdir ne '';
  584. my $wikireadmepath = "$wikipath/$wikireadmesubdir";
  585. my $readmepath = undef;
  586. if (defined $readmesubdir) {
  587. $readmepath = "$srcpath/$readmesubdir";
  588. }
  589. opendir(DH, $incpath) or die("Can't opendir '$incpath': $!\n");
  590. while (my $d = readdir(DH)) {
  591. my $dent = $d;
  592. next if not $dent =~ /$selectheaderregex/; # just selected headers.
  593. open(FH, '<', "$incpath/$dent") or die("Can't open '$incpath/$dent': $!\n");
  594. # You can optionally set a wiki category with Perl code in .wikiheaders-options that gets eval()'d per-header,
  595. # and also if you put `/* WIKI CATEGORY: blah */` on a line by itself, it'll change the category for any symbols
  596. # below it in the same file. If no category is set, one won't be added for the symbol (beyond the standard CategoryFunction, etc)
  597. my $current_wiki_category = undef;
  598. if (defined $headercategoryeval) {
  599. $_ = $dent;
  600. $current_wiki_category = eval($headercategoryeval);
  601. if (($current_wiki_category eq '') || ($current_wiki_category eq '-')) {
  602. $current_wiki_category = undef;
  603. }
  604. #print("CATEGORY FOR '$dent' IS " . (defined($current_wiki_category) ? "'$current_wiki_category'" : '(undef)') . "\n");
  605. }
  606. my @contents = ();
  607. my $ignoring_lines = 0;
  608. my $header_comment = -1;
  609. my $saw_category_doxygen = -1;
  610. my $lineno = 0;
  611. while (<FH>) {
  612. chomp;
  613. $lineno++;
  614. my $symtype = 0; # nothing, yet.
  615. my $decl;
  616. my @templines;
  617. my $str;
  618. my $has_doxygen = 1;
  619. # 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.
  620. # Functions and other things are a different story, though!
  621. if ($header_comment == -1) {
  622. $header_comment = /\A\/\*\s*\Z/ ? 1 : 0;
  623. } elsif (($header_comment == 1) && (/\A\*\/\s*\Z/)) {
  624. $header_comment = 0;
  625. }
  626. if ($ignoring_lines && /\A\s*\#\s*endif\s*\Z/) {
  627. $ignoring_lines = 0;
  628. push @contents, $_;
  629. next;
  630. } elsif ($ignoring_lines) {
  631. push @contents, $_;
  632. next;
  633. } elsif (/\A\s*\#\s*ifndef\s+SDL_WIKI_DOCUMENTATION_SECTION\s*\Z/) {
  634. $ignoring_lines = 1;
  635. push @contents, $_;
  636. next;
  637. } elsif (/\A\s*\/\*\s*WIKI CATEGORY:\s*(.*?)\s*\*\/\s*\Z/) {
  638. $current_wiki_category = (($1 eq '') || ($1 eq '-')) ? undef : $1;
  639. #print("CATEGORY FOR '$dent' CHANGED TO " . (defined($current_wiki_category) ? "'$current_wiki_category'" : '(undef)') . "\n");
  640. push @contents, $_;
  641. next;
  642. } elsif (/\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/) { # a function declaration without a doxygen comment?
  643. $symtype = 1; # function declaration
  644. @templines = ();
  645. $decl = $_;
  646. $str = '';
  647. $has_doxygen = 0;
  648. } elsif (/\A\s*SDL_FORCE_INLINE/) { # a (forced-inline) function declaration without a doxygen comment?
  649. $symtype = 1; # function declaration
  650. @templines = ();
  651. $decl = $_;
  652. $str = '';
  653. $has_doxygen = 0;
  654. } elsif (not /\A\/\*\*\s*\Z/) { # not doxygen comment start?
  655. push @contents, $_;
  656. add_coverage_gap($_, $dent, $lineno) if ($header_comment == 0);
  657. next;
  658. } else { # Start of a doxygen comment, parse it out.
  659. my $is_category_doxygen = 0;
  660. @templines = ( $_ );
  661. while (<FH>) {
  662. chomp;
  663. $lineno++;
  664. push @templines, $_;
  665. last if /\A\s*\*\/\Z/;
  666. 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...
  667. $str .= "$_\n";
  668. while (<FH>) {
  669. chomp;
  670. $lineno++;
  671. push @templines, $_;
  672. s/\A\s*\*\s?//;
  673. if (s/\A\s*\`\`\`/```/) {
  674. $str .= "$_\n";
  675. last;
  676. } else {
  677. $str .= "$_\n";
  678. }
  679. }
  680. } else {
  681. s/\A\s*\*\s*//; # Strip off the " * " at the start of the comment line.
  682. # To add documentation to Category Pages, the rule is it has to
  683. # be the first Doxygen comment in the header, and it must start with `# CategoryX`
  684. # (otherwise we'll treat it as documentation for whatever's below it). `X` is
  685. # the category name, which doesn't _necessarily_ have to match
  686. # $current_wiki_category, but it probably should.
  687. #
  688. # For compatibility with Doxygen, if there's a `\file` here instead of
  689. # `# CategoryName`, we'll accept it and use the $current_wiki_category if set.
  690. if ($saw_category_doxygen == -1) {
  691. $saw_category_doxygen = defined($current_wiki_category) && /\A\\file\s+/;
  692. if ($saw_category_doxygen) {
  693. $_ = "# Category$current_wiki_category";
  694. } else {
  695. $saw_category_doxygen = /\A\# Category/;
  696. }
  697. $is_category_doxygen = $saw_category_doxygen;
  698. }
  699. $str .= "$_\n";
  700. }
  701. }
  702. if ($is_category_doxygen) {
  703. $str =~ s/\s*\Z//;
  704. $decl = '';
  705. $symtype = -1; # not a symbol at all.
  706. } else {
  707. $decl = <FH>;
  708. $lineno++ if defined $decl;
  709. $decl = '' if not defined $decl;
  710. chomp($decl);
  711. if ($decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/) {
  712. $symtype = 1; # function declaration
  713. } elsif ($decl =~ /\A\s*SDL_FORCE_INLINE/) {
  714. $symtype = 1; # (forced-inline) function declaration
  715. } elsif ($decl =~ /\A\s*\#\s*define\s+/) {
  716. $symtype = 2; # macro
  717. } elsif ($decl =~ /\A\s*(typedef\s+|)(struct|union)/) {
  718. $symtype = 3; # struct or union
  719. } elsif ($decl =~ /\A\s*(typedef\s+|)enum/) {
  720. $symtype = 4; # enum
  721. } elsif ($decl =~ /\A\s*typedef\s+.*\Z/) {
  722. $symtype = 5; # other typedef
  723. } else {
  724. #print "Found doxygen but no function sig:\n$str\n\n";
  725. foreach (@templines) {
  726. push @contents, $_;
  727. add_coverage_gap($_, $dent, $lineno);
  728. }
  729. push @contents, $decl;
  730. add_coverage_gap($decl, $dent, $lineno);
  731. next;
  732. }
  733. }
  734. }
  735. my @paraminfo = ();
  736. my $rettype = undef;
  737. my @decllines = ( $decl );
  738. my $sym = '';
  739. if ($symtype == -1) { # category documentation with no symbol attached.
  740. @decllines = ();
  741. if ($str =~ /^#\s*Category(.*?)\s*$/m) {
  742. $sym = "[category documentation] $1"; # make a fake, unique symbol that's not valid C.
  743. } else {
  744. die("Unexpected category documentation line '$str' in '$incpath/$dent' ...?");
  745. }
  746. $headercategorydocs{$current_wiki_category} = $sym;
  747. } elsif ($symtype == 1) { # a function
  748. my $is_forced_inline = ($decl =~ /\A\s*SDL_FORCE_INLINE/);
  749. if ($is_forced_inline) {
  750. if (not $decl =~ /\)\s*(\{.*|)\s*\Z/) {
  751. while (<FH>) {
  752. chomp;
  753. $lineno++;
  754. push @decllines, $_;
  755. s/\A\s+//;
  756. s/\s+\Z//;
  757. $decl .= " $_";
  758. last if /\)\s*(\{.*|)\s*\Z/;
  759. }
  760. }
  761. $decl =~ s/\s*\)\s*(\{.*|)\s*\Z/);/;
  762. } else {
  763. if (not $decl =~ /;/) {
  764. while (<FH>) {
  765. chomp;
  766. $lineno++;
  767. push @decllines, $_;
  768. s/\A\s+//;
  769. s/\s+\Z//;
  770. $decl .= " $_";
  771. last if /;/;
  772. }
  773. }
  774. $decl =~ s/\s+\);\Z/);/;
  775. $decl =~ s/\s+;\Z/;/;
  776. }
  777. $decl =~ s/\s+\Z//;
  778. $decl = strip_fn_declaration_metadata($decl);
  779. my $paramsstr = undef;
  780. if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) {
  781. $sym = $8;
  782. $rettype = "$3$4$5$6";
  783. $paramsstr = $9;
  784. } elsif ($is_forced_inline && $decl =~ /\A\s*SDL_FORCE_INLINE\s+(SDL_DEPRECATED\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/) {
  785. $sym = $6;
  786. $rettype = "$2$3$4$5";
  787. $paramsstr = $7;
  788. } else {
  789. #print "Found doxygen but no function sig:\n$str\n\n";
  790. foreach (@templines) {
  791. push @contents, $_;
  792. }
  793. foreach (@decllines) {
  794. push @contents, $_;
  795. }
  796. next;
  797. }
  798. $rettype = sanitize_c_typename($rettype);
  799. if ($paramsstr =~ /\(/) {
  800. die("\n\n$0 FAILURE!\n" .
  801. "There's a '(' in the parameters for function '$sym' '$incpath/$dent'.\n" .
  802. "This usually means there's a parameter that's a function pointer type.\n" .
  803. "This causes problems for wikiheaders.pl and is less readable, too.\n" .
  804. "Please put that function pointer into a typedef,\n" .
  805. "and use the new type in this function's signature instead!\n\n");
  806. }
  807. my @params = split(/,/, $paramsstr);
  808. my $dotdotdot = 0;
  809. foreach (@params) {
  810. my $p = $_;
  811. $p =~ s/\A\s+//;
  812. $p =~ s/\s+\Z//;
  813. if (($p eq 'void') || ($p eq '')) {
  814. die("Void parameter in a function with multiple params?! ('$sym' in '$incpath/$dent')") if (scalar(@params) != 1);
  815. } elsif ($p eq '...') {
  816. die("Mutiple '...' params?! ('$sym' in '$incpath/$dent')") if ($dotdotdot);
  817. $dotdotdot = 1;
  818. push @paraminfo, '...';
  819. push @paraminfo, '...';
  820. } elsif ($p =~ /\A(.*)\s+([a-zA-Z0-9_\*\[\]]+)\Z/) {
  821. die("Parameter after '...' param?! ('$sym' in '$incpath/$dent')") if ($dotdotdot);
  822. my $t = $1;
  823. my $n = $2;
  824. if ($n =~ s/\A(\*+)//) {
  825. $t .= $1; # move any `*` that stuck to the name over.
  826. }
  827. if ($n =~ s/\[\]\Z//) {
  828. $t = "$t*"; # move any `[]` that stuck to the name over, as a pointer.
  829. }
  830. $t = sanitize_c_typename($t);
  831. #print("$t\n");
  832. #print("$n\n");
  833. push @paraminfo, $n;
  834. push @paraminfo, $t;
  835. } else {
  836. die("Unexpected parameter '$p' in function '$sym' in '$incpath/$dent'!");
  837. }
  838. }
  839. if (!$is_forced_inline) { # don't do with forced-inline because we don't want the implementation inserted in the wiki.
  840. $decl = ''; # rebuild this with the line breaks, since it looks better for syntax highlighting.
  841. foreach (@decllines) {
  842. if ($decl eq '') {
  843. $decl = $_;
  844. $decl =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 /;
  845. } else {
  846. my $trimmed = $_;
  847. # !!! FIXME: trim space for SDL_DEPRECATED if it was used, too.
  848. $trimmed =~ s/\A\s{28}//; # 28 for shrinking to match the removed "extern SDL_DECLSPEC SDLCALL "
  849. $decl .= $trimmed;
  850. }
  851. $decl .= "\n";
  852. }
  853. }
  854. $decl = strip_fn_declaration_metadata($decl);
  855. # !!! FIXME: code duplication with typedef processing, below.
  856. # We assume any `#define`s directly after the function are related to it: probably bitflags for an integer typedef.
  857. # We'll also allow some other basic preprocessor lines.
  858. # Blank lines are allowed, anything else, even comments, are not.
  859. my $blank_lines = 0;
  860. my $lastpos = tell(FH);
  861. my $lastlineno = $lineno;
  862. my $additional_decl = '';
  863. my $saw_define = 0;
  864. while (<FH>) {
  865. chomp;
  866. $lineno++;
  867. if (/\A\s*\Z/) {
  868. $blank_lines++;
  869. } elsif (/\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/) {
  870. if (/\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/) {
  871. $referenceonly{$1} = $sym;
  872. $saw_define = 1;
  873. } elsif (!$saw_define) {
  874. # if the first non-blank thing isn't a #define, assume we're done.
  875. seek(FH, $lastpos, 0); # re-read eaten lines again next time.
  876. $lineno = $lastlineno;
  877. last;
  878. }
  879. # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
  880. # At Sam's request, don't list property defines with functions. (See #9440)
  881. my $is_property = /\A\s*\#\s*define\s+SDL_PROP_/;
  882. if (!$is_property) {
  883. if ($blank_lines > 0) {
  884. while ($blank_lines > 0) {
  885. $additional_decl .= "\n";
  886. push @decllines, '';
  887. $blank_lines--;
  888. }
  889. }
  890. $additional_decl .= "\n$_";
  891. push @decllines, $_;
  892. $lastpos = tell(FH);
  893. }
  894. } else {
  895. seek(FH, $lastpos, 0); # re-read eaten lines again next time.
  896. $lineno = $lastlineno;
  897. last;
  898. }
  899. }
  900. $decl .= $additional_decl;
  901. } elsif ($symtype == 2) { # a macro
  902. if ($decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)\s+/) {
  903. $sym = $1;
  904. } else {
  905. #print "Found doxygen but no macro:\n$str\n\n";
  906. foreach (@templines) {
  907. push @contents, $_;
  908. }
  909. foreach (@decllines) {
  910. push @contents, $_;
  911. }
  912. next;
  913. }
  914. while ($decl =~ /\\\Z/) {
  915. my $l = <FH>;
  916. last if not $l;
  917. $lineno++;
  918. chomp($l);
  919. push @decllines, $l;
  920. #$l =~ s/\A\s+//;
  921. $l =~ s/\s+\Z//;
  922. $decl .= "\n$l";
  923. }
  924. } elsif (($symtype == 3) || ($symtype == 4)) { # struct or union or enum
  925. my $has_definition = 0;
  926. if ($decl =~ /\A\s*(typedef\s+|)(struct|union|enum)\s*(.*?)\s*(\n|\{|\;|\Z)/) {
  927. my $ctype = $2;
  928. my $origsym = $3;
  929. my $ending = $4;
  930. $sym = $origsym;
  931. if ($sym =~ s/\A(.*?)(\s+)(.*?)\Z/$1/) {
  932. die("Failed to parse '$origsym' correctly!") if ($sym ne $1); # Thought this was "typedef struct MySym MySym;" ... it was not. :( This is a hack!
  933. }
  934. if ($sym eq '') {
  935. die("\n\n$0 FAILURE!\n" .
  936. "There's a 'typedef $ctype' in $incpath/$dent without a name at the top.\n" .
  937. "Instead of `typedef $ctype {} x;`, this should be `typedef $ctype x {} x;`.\n" .
  938. "This causes problems for wikiheaders.pl and scripting language bindings.\n" .
  939. "Please fix it!\n\n");
  940. }
  941. $has_definition = ($ending ne ';');
  942. } else {
  943. #print "Found doxygen but no datatype:\n$str\n\n";
  944. foreach (@templines) {
  945. push @contents, $_;
  946. }
  947. foreach (@decllines) {
  948. push @contents, $_;
  949. }
  950. next;
  951. }
  952. # This block attempts to find the whole struct/union/enum definition by counting matching brackets. Kind of yucky.
  953. if ($has_definition) {
  954. my $started = 0;
  955. my $brackets = 0;
  956. my $pending = $decl;
  957. $decl = '';
  958. while (!$started || ($brackets != 0)) {
  959. foreach my $seg (split(/([{}])/, $pending)) {
  960. $decl .= $seg;
  961. if ($seg eq '{') {
  962. $started = 1;
  963. $brackets++;
  964. } elsif ($seg eq '}') {
  965. die("Something is wrong with header $incpath/$dent while parsing $sym; is a bracket missing?\n") if ($brackets <= 0);
  966. $brackets--;
  967. }
  968. }
  969. if (!$started || ($brackets != 0)) {
  970. $pending = <FH>;
  971. die("EOF/error reading $incpath/$dent while parsing $sym\n") if not $pending;
  972. $lineno++;
  973. chomp($pending);
  974. push @decllines, $pending;
  975. $decl .= "\n";
  976. }
  977. }
  978. # 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!
  979. }
  980. } elsif ($symtype == 5) { # other typedef
  981. if ($decl =~ /\A\s*typedef\s+(.*)\Z/) {
  982. my $tdstr = $1;
  983. if (not $decl =~ /;/) {
  984. while (<FH>) {
  985. chomp;
  986. $lineno++;
  987. push @decllines, $_;
  988. s/\A\s+//;
  989. s/\s+\Z//;
  990. $decl .= " $_";
  991. last if /;/;
  992. }
  993. }
  994. $decl =~ s/\s+(\))?;\Z/$1;/;
  995. $tdstr =~ s/;\s*\Z//;
  996. #my $datatype;
  997. if ($tdstr =~ /\A(.*?)\s*\((.*?)\s*\*\s*(.*?)\)\s*\((.*?)(\))?/) { # a function pointer type
  998. $sym = $3;
  999. #$datatype = "$1 ($2 *$sym)($4)";
  1000. } elsif ($tdstr =~ /\A(.*[\s\*]+)(.*?)\s*\Z/) {
  1001. $sym = $2;
  1002. #$datatype = $1;
  1003. } else {
  1004. die("Failed to parse typedef '$tdstr' in $incpath/$dent!\n"); # I'm hitting a C grammar nail with a regexp hammer here, y'all.
  1005. }
  1006. $sym =~ s/\A\s+//;
  1007. $sym =~ s/\s+\Z//;
  1008. #$datatype =~ s/\A\s+//;
  1009. #$datatype =~ s/\s+\Z//;
  1010. } else {
  1011. #print "Found doxygen but no datatype:\n$str\n\n";
  1012. foreach (@templines) {
  1013. push @contents, $_;
  1014. }
  1015. foreach (@decllines) {
  1016. push @contents, $_;
  1017. }
  1018. next;
  1019. }
  1020. # We assume any `#define`s directly after the typedef are related to it: probably bitflags for an integer typedef.
  1021. # We'll also allow some other basic preprocessor lines.
  1022. # Blank lines are allowed, anything else, even comments, are not.
  1023. my $blank_lines = 0;
  1024. my $lastpos = tell(FH);
  1025. my $lastlineno = $lineno;
  1026. my $additional_decl = '';
  1027. my $saw_define = 0;
  1028. while (<FH>) {
  1029. chomp;
  1030. $lineno++;
  1031. if (/\A\s*\Z/) {
  1032. $blank_lines++;
  1033. } elsif (/\A\s*\#\s*(define|if|else|elif|endif)(\s+|\Z)/) {
  1034. if (/\A\s*\#\s*define\s+([a-zA-Z0-9_]*)/) {
  1035. $referenceonly{$1} = $sym;
  1036. $saw_define = 1;
  1037. } elsif (!$saw_define) {
  1038. # if the first non-blank thing isn't a #define, assume we're done.
  1039. seek(FH, $lastpos, 0); # re-read eaten lines again next time.
  1040. $lineno = $lastlineno;
  1041. last;
  1042. }
  1043. # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
  1044. if ($blank_lines > 0) {
  1045. while ($blank_lines > 0) {
  1046. $additional_decl .= "\n";
  1047. push @decllines, '';
  1048. $blank_lines--;
  1049. }
  1050. }
  1051. $additional_decl .= "\n$_";
  1052. push @decllines, $_;
  1053. $lastpos = tell(FH);
  1054. } else {
  1055. seek(FH, $lastpos, 0); # re-read eaten lines again next time.
  1056. $lineno = $lastlineno;
  1057. last;
  1058. }
  1059. }
  1060. $decl .= $additional_decl;
  1061. } else {
  1062. die("Unexpected symtype $symtype");
  1063. }
  1064. #print("DECL: [$decl]\n");
  1065. #print("$sym:\n$str\n\n");
  1066. # There might be multiple declarations of a function due to #ifdefs,
  1067. # and only one of them will have documentation. If we hit an
  1068. # undocumented one before, delete the placeholder line we left for
  1069. # it so it doesn't accumulate a new blank line on each run.
  1070. my $skipsym = 0;
  1071. if (defined $headersymshasdoxygen{$sym}) {
  1072. if ($headersymshasdoxygen{$sym} == 0) { # An undocumented declaration already exists, nuke its placeholder line.
  1073. delete $contents[$headersymschunk{$sym}]; # delete DOES NOT RENUMBER existing elements!
  1074. } else { # documented function already existed?
  1075. $skipsym = 1; # don't add this copy to the list of functions.
  1076. if ($has_doxygen) {
  1077. print STDERR "WARNING: Symbol '$sym' appears to be documented in multiple locations. Only keeping the first one we saw!\n";
  1078. }
  1079. push @contents, join("\n", @decllines) if (scalar(@decllines) > 0); # just put the existing declation in as-is.
  1080. }
  1081. }
  1082. if (!$skipsym) {
  1083. $headersymscategory{$sym} = $current_wiki_category if defined $current_wiki_category;
  1084. $headersyms{$sym} = $str;
  1085. $headerdecls{$sym} = $decl;
  1086. $headersymslocation{$sym} = $dent;
  1087. $headersymschunk{$sym} = scalar(@contents);
  1088. $headersymshasdoxygen{$sym} = $has_doxygen;
  1089. $headersymstype{$sym} = $symtype;
  1090. $headersymsparaminfo{$sym} = \@paraminfo if (scalar(@paraminfo) > 0);
  1091. $headersymsrettype{$sym} = $rettype if (defined($rettype));
  1092. push @contents, join("\n", @templines);
  1093. push @contents, join("\n", @decllines) if (scalar(@decllines) > 0);
  1094. }
  1095. }
  1096. close(FH);
  1097. $headers{$dent} = \@contents;
  1098. }
  1099. closedir(DH);
  1100. opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n");
  1101. while (my $d = readdir(DH)) {
  1102. my $dent = $d;
  1103. my $type = '';
  1104. if ($dent =~ /\.(md|mediawiki)\Z/) {
  1105. $type = $1;
  1106. } else {
  1107. next; # only dealing with wiki pages.
  1108. }
  1109. my $sym = $dent;
  1110. $sym =~ s/\..*\Z//;
  1111. # (There are other pages to ignore, but these are known ones to not bother parsing.)
  1112. # Ignore FrontPage.
  1113. next if $sym eq 'FrontPage';
  1114. open(FH, '<', "$wikipath/$dent") or die("Can't open '$wikipath/$dent': $!\n");
  1115. if ($sym =~ /\ACategory(.*?)\Z/) { # Special case for Category pages.
  1116. # Find the end of the category documentation in the existing file and append everything else to the new file.
  1117. my $cat = $1;
  1118. my $docstr = '';
  1119. my $notdocstr = '';
  1120. my $docs = 1;
  1121. while (<FH>) {
  1122. chomp;
  1123. if ($docs) {
  1124. $docs = 0 if /\A\-\-\-\-\Z/; # Hit a footer? We're done.
  1125. $docs = 0 if /\A<!\-\-/; # Hit an HTML comment? We're done.
  1126. }
  1127. if ($docs) {
  1128. $docstr .= "$_\n";
  1129. } else {
  1130. $notdocstr .= "$_\n";
  1131. }
  1132. }
  1133. close(FH);
  1134. $docstr =~ s/\s*\Z//;
  1135. $sym = "[category documentation] $cat"; # make a fake, unique symbol that's not valid C.
  1136. $wikitypes{$sym} = $type;
  1137. my %sections = ();
  1138. $sections{'Remarks'} = $docstr;
  1139. $sections{'[footer]'} = $notdocstr;
  1140. $wikisyms{$sym} = \%sections;
  1141. my @section_order = ( 'Remarks', '[footer]' );
  1142. $wikisectionorder{$sym} = \@section_order;
  1143. next;
  1144. }
  1145. my $current_section = '[start]';
  1146. my @section_order = ( $current_section );
  1147. my %sections = ();
  1148. $sections{$current_section} = '';
  1149. my $firstline = 1;
  1150. while (<FH>) {
  1151. chomp;
  1152. my $orig = $_;
  1153. s/\A\s*//;
  1154. s/\s*\Z//;
  1155. if ($type eq 'mediawiki') {
  1156. if (defined($wikipreamble) && $firstline && /\A\=\=\=\=\=\= (.*?) \=\=\=\=\=\=\Z/ && ($1 eq $wikipreamble)) {
  1157. $firstline = 0; # skip this.
  1158. next;
  1159. } elsif (/\A\= (.*?) \=\Z/) {
  1160. $firstline = 0;
  1161. $current_section = ($1 eq $sym) ? '[Brief]' : $1;
  1162. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  1163. push @section_order, $current_section;
  1164. $sections{$current_section} = '';
  1165. } elsif (/\A\=\= (.*?) \=\=\Z/) {
  1166. $firstline = 0;
  1167. $current_section = ($1 eq $sym) ? '[Brief]' : $1;
  1168. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  1169. push @section_order, $current_section;
  1170. $sections{$current_section} = '';
  1171. next;
  1172. } elsif (/\A\-\-\-\-\Z/) {
  1173. $firstline = 0;
  1174. $current_section = '[footer]';
  1175. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  1176. push @section_order, $current_section;
  1177. $sections{$current_section} = '';
  1178. next;
  1179. }
  1180. } elsif ($type eq 'md') {
  1181. if (defined($wikipreamble) && $firstline && /\A\#\#\#\#\#\# (.*?)\Z/ && ($1 eq $wikipreamble)) {
  1182. $firstline = 0; # skip this.
  1183. next;
  1184. } elsif (/\A\#+ (.*?)\Z/) {
  1185. $firstline = 0;
  1186. $current_section = ($1 eq $sym) ? '[Brief]' : $1;
  1187. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  1188. push @section_order, $current_section;
  1189. $sections{$current_section} = '';
  1190. next;
  1191. } elsif (/\A\-\-\-\-\Z/) {
  1192. $firstline = 0;
  1193. $current_section = '[footer]';
  1194. die("Doubly-defined section '$current_section' in '$dent'!\n") if defined $sections{$current_section};
  1195. push @section_order, $current_section;
  1196. $sections{$current_section} = '';
  1197. next;
  1198. }
  1199. } else {
  1200. die("Unexpected wiki file type. Fixme!");
  1201. }
  1202. if ($firstline) {
  1203. $firstline = ($_ ne '');
  1204. }
  1205. if (!$firstline) {
  1206. $sections{$current_section} .= "$orig\n";
  1207. }
  1208. }
  1209. close(FH);
  1210. foreach (keys %sections) {
  1211. $sections{$_} =~ s/\A\n+//;
  1212. $sections{$_} =~ s/\n+\Z//;
  1213. $sections{$_} .= "\n";
  1214. }
  1215. # older section name we used, migrate over from it.
  1216. if (defined $sections{'Related Functions'}) {
  1217. if (not defined $sections{'See Also'}) {
  1218. $sections{'See Also'} = $sections{'Related Functions'};
  1219. }
  1220. delete $sections{'Related Functions'};
  1221. }
  1222. if (0) {
  1223. foreach (@section_order) {
  1224. print("$sym SECTION '$_':\n");
  1225. print($sections{$_});
  1226. print("\n\n");
  1227. }
  1228. }
  1229. $wikitypes{$sym} = $type;
  1230. $wikisyms{$sym} = \%sections;
  1231. $wikisectionorder{$sym} = \@section_order;
  1232. }
  1233. closedir(DH);
  1234. delete $wikisyms{"Undocumented"};
  1235. {
  1236. my $path = "$wikipath/Undocumented.md";
  1237. open(my $fh, '>', $path) or die("Can't open '$path': $!\n");
  1238. print $fh "# Undocumented\n\n";
  1239. print_undocumented_section($fh, 'Functions', 1);
  1240. #print_undocumented_section($fh, 'Macros', 2);
  1241. close($fh);
  1242. }
  1243. if ($warn_about_missing) {
  1244. foreach (keys %wikisyms) {
  1245. my $sym = $_;
  1246. if (not defined $headersyms{$sym}) {
  1247. print STDERR "WARNING: $sym defined in the wiki but not the headers!\n";
  1248. }
  1249. }
  1250. foreach (keys %headersyms) {
  1251. my $sym = $_;
  1252. if (not defined $wikisyms{$sym}) {
  1253. print STDERR "WARNING: $sym defined in the headers but not the wiki!\n";
  1254. }
  1255. }
  1256. }
  1257. if ($copy_direction == 1) { # --copy-to-headers
  1258. my %changed_headers = ();
  1259. $dewikify_mode = 'md';
  1260. $wordwrap_mode = 'md'; # the headers use Markdown format.
  1261. foreach (keys %headersyms) {
  1262. my $sym = $_;
  1263. next if not defined $wikisyms{$sym}; # don't have a page for that function, skip it.
  1264. my $symtype = $headersymstype{$sym};
  1265. my $wikitype = $wikitypes{$sym};
  1266. my $sectionsref = $wikisyms{$sym};
  1267. my $remarks = $sectionsref->{'Remarks'};
  1268. my $returns = $sectionsref->{'Return Value'};
  1269. my $threadsafety = $sectionsref->{'Thread Safety'};
  1270. my $version = $sectionsref->{'Version'};
  1271. my $related = $sectionsref->{'See Also'};
  1272. my $deprecated = $sectionsref->{'Deprecated'};
  1273. my $brief = $sectionsref->{'[Brief]'};
  1274. my $addblank = 0;
  1275. my $str = '';
  1276. my $params = undef;
  1277. my $paramstr = undef;
  1278. if ($symtype == -1) { # category documentation block.
  1279. # nothing to be done here.
  1280. } elsif (($symtype == 1) || (($symtype == 5))) { # we'll assume a typedef (5) with a \param is a function pointer typedef.
  1281. $params = $sectionsref->{'Function Parameters'};
  1282. $paramstr = '\param';
  1283. } elsif ($symtype == 2) {
  1284. $params = $sectionsref->{'Macro Parameters'};
  1285. $paramstr = '\param';
  1286. } elsif ($symtype == 3) {
  1287. $params = $sectionsref->{'Fields'};
  1288. $paramstr = '\field';
  1289. } elsif ($symtype == 4) {
  1290. $params = $sectionsref->{'Values'};
  1291. $paramstr = '\value';
  1292. } else {
  1293. die("Unexpected symtype $symtype");
  1294. }
  1295. $headersymshasdoxygen{$sym} = 1; # Added/changed doxygen for this header.
  1296. $brief = dewikify($wikitype, $brief);
  1297. $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  1298. my @briefsplit = split /\n/, $brief;
  1299. $brief = shift @briefsplit;
  1300. if (defined $remarks) {
  1301. $remarks = join("\n", @briefsplit) . dewikify($wikitype, $remarks);
  1302. }
  1303. if (defined $brief) {
  1304. $str .= "\n" if $addblank; $addblank = 1;
  1305. $str .= wordwrap($brief) . "\n";
  1306. }
  1307. if (defined $remarks) {
  1308. $str .= "\n" if $addblank; $addblank = 1;
  1309. $str .= wordwrap($remarks) . "\n";
  1310. }
  1311. if (defined $deprecated) {
  1312. # !!! FIXME: lots of code duplication in all of these.
  1313. $str .= "\n" if $addblank; $addblank = 1;
  1314. my $v = dewikify($wikitype, $deprecated);
  1315. my $whitespacelen = length("\\deprecated") + 1;
  1316. my $whitespace = ' ' x $whitespacelen;
  1317. $v = wordwrap($v, -$whitespacelen);
  1318. my @desclines = split /\n/, $v;
  1319. my $firstline = shift @desclines;
  1320. $str .= "\\deprecated $firstline\n";
  1321. foreach (@desclines) {
  1322. $str .= "${whitespace}$_\n";
  1323. }
  1324. }
  1325. if (defined $params) {
  1326. $str .= "\n" if $addblank; $addblank = (defined $returns) ? 0 : 1;
  1327. my @lines = split /\n/, dewikify($wikitype, $params);
  1328. if ($wikitype eq 'mediawiki') {
  1329. die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start
  1330. while (scalar(@lines) >= 3) {
  1331. my $c_datatype = shift @lines;
  1332. my $name = shift @lines;
  1333. my $desc = shift @lines;
  1334. my $terminator; # the '|-' or '|}' line.
  1335. if (($desc eq '|-') or ($desc eq '|}') or (not $desc =~ /\A\|/)) { # we seem to be out of cells, which means there was no datatype column on this one.
  1336. $terminator = $desc;
  1337. $desc = $name;
  1338. $name = $c_datatype;
  1339. $c_datatype = '';
  1340. } else {
  1341. $terminator = shift @lines;
  1342. }
  1343. last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table.
  1344. $name =~ s/\A\|\s*//;
  1345. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  1346. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  1347. $desc =~ s/\A\|\s*//;
  1348. #print STDERR "SYM: $sym CDATATYPE: $c_datatype NAME: $name DESC: $desc TERM: $terminator\n";
  1349. my $whitespacelen = length($name) + 8;
  1350. my $whitespace = ' ' x $whitespacelen;
  1351. $desc = wordwrap($desc, -$whitespacelen);
  1352. my @desclines = split /\n/, $desc;
  1353. my $firstline = shift @desclines;
  1354. $str .= "$paramstr $name $firstline\n";
  1355. foreach (@desclines) {
  1356. $str .= "${whitespace}$_\n";
  1357. }
  1358. }
  1359. } elsif ($wikitype eq 'md') {
  1360. my $l;
  1361. $l = shift @lines;
  1362. die("Unexpected data parsing Markdown table") if (not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/);
  1363. $l = shift @lines;
  1364. die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/);
  1365. while (scalar(@lines) >= 1) {
  1366. $l = shift @lines;
  1367. my $name;
  1368. my $desc;
  1369. if ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) {
  1370. # c datatype is $1, but we don't care about it here.
  1371. $name = $2;
  1372. $desc = $3;
  1373. } elsif ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) {
  1374. $name = $1;
  1375. $desc = $2;
  1376. } else {
  1377. last; # we seem to have run out of table.
  1378. }
  1379. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  1380. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  1381. #print STDERR "SYM: $sym NAME: $name DESC: $desc\n";
  1382. my $whitespacelen = length($name) + 8;
  1383. my $whitespace = ' ' x $whitespacelen;
  1384. $desc = wordwrap($desc, -$whitespacelen);
  1385. my @desclines = split /\n/, $desc;
  1386. my $firstline = shift @desclines;
  1387. $str .= "$paramstr $name $firstline\n";
  1388. foreach (@desclines) {
  1389. $str .= "${whitespace}$_\n";
  1390. }
  1391. }
  1392. } else {
  1393. die("write me");
  1394. }
  1395. }
  1396. if (defined $returns) {
  1397. $str .= "\n" if $addblank; $addblank = 1;
  1398. my $r = dewikify($wikitype, $returns);
  1399. $r =~ s/\A\(.*?\)\s*//; # Chop datatype in parentheses off the front.
  1400. my $retstr = "\\returns";
  1401. if ($r =~ s/\AReturn(s?)\s+//) {
  1402. $retstr = "\\return$1";
  1403. }
  1404. my $whitespacelen = length($retstr) + 1;
  1405. my $whitespace = ' ' x $whitespacelen;
  1406. $r = wordwrap($r, -$whitespacelen);
  1407. my @desclines = split /\n/, $r;
  1408. my $firstline = shift @desclines;
  1409. $str .= "$retstr $firstline\n";
  1410. foreach (@desclines) {
  1411. $str .= "${whitespace}$_\n";
  1412. }
  1413. }
  1414. if (defined $threadsafety) {
  1415. # !!! FIXME: lots of code duplication in all of these.
  1416. $str .= "\n" if $addblank; $addblank = 1;
  1417. my $v = dewikify($wikitype, $threadsafety);
  1418. my $whitespacelen = length("\\threadsafety") + 1;
  1419. my $whitespace = ' ' x $whitespacelen;
  1420. $v = wordwrap($v, -$whitespacelen);
  1421. my @desclines = split /\n/, $v;
  1422. my $firstline = shift @desclines;
  1423. $str .= "\\threadsafety $firstline\n";
  1424. foreach (@desclines) {
  1425. $str .= "${whitespace}$_\n";
  1426. }
  1427. }
  1428. if (defined $version) {
  1429. # !!! FIXME: lots of code duplication in all of these.
  1430. $str .= "\n" if $addblank; $addblank = 1;
  1431. my $v = dewikify($wikitype, $version);
  1432. my $whitespacelen = length("\\since") + 1;
  1433. my $whitespace = ' ' x $whitespacelen;
  1434. $v = wordwrap($v, -$whitespacelen);
  1435. my @desclines = split /\n/, $v;
  1436. my $firstline = shift @desclines;
  1437. $str .= "\\since $firstline\n";
  1438. foreach (@desclines) {
  1439. $str .= "${whitespace}$_\n";
  1440. }
  1441. }
  1442. if (defined $related) {
  1443. # !!! FIXME: lots of code duplication in all of these.
  1444. $str .= "\n" if $addblank; $addblank = 1;
  1445. my $v = dewikify($wikitype, $related);
  1446. my @desclines = split /\n/, $v;
  1447. foreach (@desclines) {
  1448. s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func"
  1449. s/\[\[(.*?)\]\]/$1/; # in case some wikilinks remain.
  1450. s/\[(.*?)\]\(.*?\)/$1/; # in case some wikilinks remain.
  1451. s/\A\/*//;
  1452. s/\A\s*[\:\*\-]\s*//;
  1453. s/\A\s+//;
  1454. s/\s+\Z//;
  1455. $str .= "\\sa $_\n";
  1456. }
  1457. }
  1458. my $header = $headersymslocation{$sym};
  1459. my $contentsref = $headers{$header};
  1460. my $chunk = $headersymschunk{$sym};
  1461. my @lines = split /\n/, $str;
  1462. my $addnewline = (($chunk > 0) && ($$contentsref[$chunk-1] ne '')) ? "\n" : '';
  1463. my $output = "$addnewline/**\n";
  1464. foreach (@lines) {
  1465. chomp;
  1466. s/\s*\Z//;
  1467. if ($_ eq '') {
  1468. $output .= " *\n";
  1469. } else {
  1470. $output .= " * $_\n";
  1471. }
  1472. }
  1473. $output .= " */";
  1474. #print("$sym:\n[$output]\n\n");
  1475. $$contentsref[$chunk] = $output;
  1476. #$$contentsref[$chunk+1] = $headerdecls{$sym};
  1477. $changed_headers{$header} = 1;
  1478. }
  1479. foreach (keys %changed_headers) {
  1480. my $header = $_;
  1481. # this is kinda inefficient, but oh well.
  1482. my @removelines = ();
  1483. foreach (keys %headersymslocation) {
  1484. my $sym = $_;
  1485. next if $headersymshasdoxygen{$sym};
  1486. next if $headersymslocation{$sym} ne $header;
  1487. # 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.
  1488. push @removelines, $headersymschunk{$sym};
  1489. }
  1490. my $contentsref = $headers{$header};
  1491. foreach (@removelines) {
  1492. delete $$contentsref[$_]; # delete DOES NOT RENUMBER existing elements!
  1493. }
  1494. my $path = "$incpath/$header.tmp";
  1495. open(FH, '>', $path) or die("Can't open '$path': $!\n");
  1496. foreach (@$contentsref) {
  1497. print FH "$_\n" if defined $_;
  1498. }
  1499. close(FH);
  1500. rename($path, "$incpath/$header") or die("Can't rename '$path' to '$incpath/$header': $!\n");
  1501. }
  1502. if (defined $readmepath) {
  1503. if ( -d $wikireadmepath ) {
  1504. mkdir($readmepath); # just in case
  1505. opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n");
  1506. while (readdir(DH)) {
  1507. my $dent = $_;
  1508. if ($dent =~ /\A(.*?)\.md\Z/) { # we only bridge Markdown files here.
  1509. next if $1 eq 'FrontPage';
  1510. filecopy("$wikireadmepath/$dent", "$readmepath/README-$dent", "\n");
  1511. }
  1512. }
  1513. closedir(DH);
  1514. }
  1515. }
  1516. } elsif ($copy_direction == -1) { # --copy-to-wiki
  1517. if (defined $changeformat) {
  1518. $dewikify_mode = $changeformat;
  1519. $wordwrap_mode = $changeformat;
  1520. }
  1521. foreach (keys %headersyms) {
  1522. my $sym = $_;
  1523. next if not $headersymshasdoxygen{$sym};
  1524. next if $sym =~ /\A\[category documentation\]/; # not real symbols, we handle this elsewhere.
  1525. my $symtype = $headersymstype{$sym};
  1526. my $origwikitype = defined $wikitypes{$sym} ? $wikitypes{$sym} : 'md'; # default to MarkDown for new stuff.
  1527. my $wikitype = (defined $changeformat) ? $changeformat : $origwikitype;
  1528. die("Unexpected wikitype '$wikitype'") if (($wikitype ne 'mediawiki') and ($wikitype ne 'md') and ($wikitype ne 'manpage'));
  1529. #print("$sym\n"); next;
  1530. $wordwrap_mode = $wikitype;
  1531. my $raw = $headersyms{$sym}; # raw doxygen text with comment characters stripped from start/end and start of each line.
  1532. next if not defined $raw;
  1533. $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.
  1534. my @doxygenlines = split /\n/, $raw;
  1535. my $brief = '';
  1536. while (@doxygenlines) {
  1537. last if $doxygenlines[0] =~ /\A\\/; # some sort of doxygen command, assume we're past the general remarks.
  1538. last if $doxygenlines[0] =~ /\A\s*\Z/; # blank line? End of paragraph, done.
  1539. my $l = shift @doxygenlines;
  1540. chomp($l);
  1541. $l =~ s/\A\s*//;
  1542. $l =~ s/\s*\Z//;
  1543. $brief .= "$l ";
  1544. }
  1545. $brief =~ s/\s+\Z//;
  1546. $brief =~ s/\A(.*?\.) /$1\n\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  1547. my @briefsplit = split /\n/, $brief;
  1548. next if not defined $briefsplit[0]; # No brief text? Probably a bogus Doxygen comment, skip it.
  1549. $brief = wikify($wikitype, shift @briefsplit) . "\n";
  1550. @doxygenlines = (@briefsplit, @doxygenlines);
  1551. my $remarks = '';
  1552. while (@doxygenlines) {
  1553. last if $doxygenlines[0] =~ /\A\\/; # some sort of doxygen command, assume we're past the general remarks.
  1554. my $l = shift @doxygenlines;
  1555. $remarks .= "$l\n";
  1556. }
  1557. #print("REMARKS:\n\n $remarks\n\n");
  1558. $remarks = wordwrap(wikify($wikitype, $remarks));
  1559. $remarks =~ s/\A\s*//;
  1560. $remarks =~ s/\s*\Z//;
  1561. my $decl = $headerdecls{$sym};
  1562. my $syntax = '';
  1563. if ($wikitype eq 'mediawiki') {
  1564. $syntax = "<syntaxhighlight lang='c'>\n$decl</syntaxhighlight>\n";
  1565. } elsif ($wikitype eq 'md') {
  1566. $decl =~ s/\n+\Z//;
  1567. $syntax = "```c\n$decl\n```\n";
  1568. } else { die("Expected wikitype '$wikitype'"); }
  1569. my %sections = ();
  1570. $sections{'[Brief]'} = $brief; # include this section even if blank so we get a title line.
  1571. $sections{'Remarks'} = "$remarks\n" if $remarks ne '';
  1572. $sections{'Syntax'} = $syntax;
  1573. my %params = (); # have to parse these and build up the wiki tables after, since Markdown needs to know the length of the largest string. :/
  1574. my @paramsorder = ();
  1575. my $fnsigparams = $headersymsparaminfo{$sym};
  1576. while (@doxygenlines) {
  1577. my $l = shift @doxygenlines;
  1578. # We allow param/field/value interchangeably, even if it doesn't make sense. The next --copy-to-headers will correct it anyhow.
  1579. if ($l =~ /\A\\(param|field|value)\s+(.*?)\s+(.*)\Z/) {
  1580. my $arg = $2;
  1581. my $desc = $3;
  1582. while (@doxygenlines) {
  1583. my $subline = $doxygenlines[0];
  1584. $subline =~ s/\A\s*//;
  1585. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1586. shift @doxygenlines; # dump this line from the array; we're using it.
  1587. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1588. $desc .= "\n";
  1589. } else {
  1590. $desc .= " $subline";
  1591. }
  1592. }
  1593. $desc =~ s/[\s\n]+\Z//ms;
  1594. # Validate this param.
  1595. if (defined($params{$arg})) {
  1596. print STDERR "WARNING: Symbol '$sym' has multiple '\\param $arg' declarations! Only keeping the first one!\n";
  1597. } elsif (defined $fnsigparams) {
  1598. my $found = 0;
  1599. for (my $i = 0; $i < scalar(@$fnsigparams); $i += 2) {
  1600. $found = 1, last if (@$fnsigparams[$i] eq $arg);
  1601. }
  1602. if (!$found) {
  1603. print STDERR "WARNING: Symbol '$sym' has a '\\param $arg' for a param that doesn't exist. It will be removed!\n";
  1604. }
  1605. }
  1606. # We need to know the length of the longest string to make Markdown tables, so we just store these off until everything is parsed.
  1607. $params{$arg} = $desc;
  1608. push @paramsorder, $arg;
  1609. } elsif ($l =~ /\A\\r(eturns?)\s+(.*)\Z/) {
  1610. # !!! FIXME: complain if this isn't a function or macro.
  1611. my $retstr = "R$1"; # "Return" or "Returns"
  1612. my $desc = $2;
  1613. while (@doxygenlines) {
  1614. my $subline = $doxygenlines[0];
  1615. $subline =~ s/\A\s*//;
  1616. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1617. shift @doxygenlines; # dump this line from the array; we're using it.
  1618. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1619. $desc .= "\n";
  1620. } else {
  1621. $desc .= " $subline";
  1622. }
  1623. }
  1624. $desc =~ s/[\s\n]+\Z//ms;
  1625. # Make sure the \returns info is valid.
  1626. my $rettype = $headersymsrettype{$sym};
  1627. die("Don't have a rettype for '$sym' for some reason!") if (($symtype == 1) && (not defined($rettype)));
  1628. if (defined($sections{'Return Value'})) {
  1629. print STDERR "WARNING: Symbol '$sym' has multiple '\\return' declarations! Only keeping the first one!\n";
  1630. } elsif (($symtype != 1) && ($symtype != 2) && ($symtype != 5)) { # !!! FIXME: if 5, make sure it's a function pointer typedef!
  1631. print STDERR "WARNING: Symbol '$sym' has a '\\return' declaration but isn't a function or macro! Removing it!\n";
  1632. } elsif (($symtype == 1) && ($headersymsrettype{$sym} eq 'void')) {
  1633. print STDERR "WARNING: Function '$sym' has a '\\returns' declaration but function returns void! Removing it!\n";
  1634. } else {
  1635. my $rettypestr = defined($rettype) ? ('(' . wikify($wikitype, $rettype) . ') ') : '';
  1636. $sections{'Return Value'} = wordwrap("$rettypestr$retstr ". wikify($wikitype, $desc)) . "\n";
  1637. }
  1638. } elsif ($l =~ /\A\\deprecated\s+(.*)\Z/) {
  1639. my $desc = $1;
  1640. while (@doxygenlines) {
  1641. my $subline = $doxygenlines[0];
  1642. $subline =~ s/\A\s*//;
  1643. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1644. shift @doxygenlines; # dump this line from the array; we're using it.
  1645. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1646. $desc .= "\n";
  1647. } else {
  1648. $desc .= " $subline";
  1649. }
  1650. }
  1651. $desc =~ s/[\s\n]+\Z//ms;
  1652. $sections{'Deprecated'} = wordwrap(wikify($wikitype, $desc)) . "\n";
  1653. } elsif ($l =~ /\A\\since\s+(.*)\Z/) {
  1654. my $desc = $1;
  1655. while (@doxygenlines) {
  1656. my $subline = $doxygenlines[0];
  1657. $subline =~ s/\A\s*//;
  1658. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1659. shift @doxygenlines; # dump this line from the array; we're using it.
  1660. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1661. $desc .= "\n";
  1662. } else {
  1663. $desc .= " $subline";
  1664. }
  1665. }
  1666. $desc =~ s/[\s\n]+\Z//ms;
  1667. $sections{'Version'} = wordwrap(wikify($wikitype, $desc)) . "\n";
  1668. } elsif ($l =~ /\A\\threadsafety\s+(.*)\Z/) {
  1669. my $desc = $1;
  1670. while (@doxygenlines) {
  1671. my $subline = $doxygenlines[0];
  1672. $subline =~ s/\A\s*//;
  1673. last if $subline =~ /\A\\/; # some sort of doxygen command, assume we're past this thing.
  1674. shift @doxygenlines; # dump this line from the array; we're using it.
  1675. if ($subline eq '') { # empty line, make sure it keeps the newline char.
  1676. $desc .= "\n";
  1677. } else {
  1678. $desc .= " $subline";
  1679. }
  1680. }
  1681. $desc =~ s/[\s\n]+\Z//ms;
  1682. $sections{'Thread Safety'} = wordwrap(wikify($wikitype, $desc)) . "\n";
  1683. } elsif ($l =~ /\A\\sa\s+(.*)\Z/) {
  1684. my $sa = $1;
  1685. $sa =~ s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func"
  1686. $sections{'See Also'} = '' if not defined $sections{'See Also'};
  1687. if ($wikitype eq 'mediawiki') {
  1688. $sections{'See Also'} .= ":[[$sa]]\n";
  1689. } elsif ($wikitype eq 'md') {
  1690. $sections{'See Also'} .= "- [$sa]($sa)\n";
  1691. } else { die("Expected wikitype '$wikitype'"); }
  1692. }
  1693. }
  1694. # Make sure %params is in the same order as the actual function signature and add C datatypes...
  1695. my $params_has_c_datatype = 0;
  1696. my @final_params = ();
  1697. if (($symtype == 1) && (defined($headersymsparaminfo{$sym}))) { # is a function and we have param info for it...
  1698. my $fnsigparams = $headersymsparaminfo{$sym};
  1699. for (my $i = 0; $i < scalar(@$fnsigparams); $i += 2) {
  1700. my $paramname = @$fnsigparams[$i];
  1701. my $paramdesc = $params{$paramname};
  1702. if (defined($paramdesc)) {
  1703. push @final_params, $paramname; # name
  1704. push @final_params, @$fnsigparams[$i+1]; # C datatype
  1705. push @final_params, $paramdesc; # description
  1706. $params_has_c_datatype = 1 if (defined(@$fnsigparams[$i+1]));
  1707. } else {
  1708. print STDERR "WARNING: Symbol '$sym' is missing a '\\param $paramname' declaration!\n";
  1709. }
  1710. }
  1711. } else {
  1712. foreach (@paramsorder) {
  1713. my $paramname = $_;
  1714. my $paramdesc = $params{$paramname};
  1715. if (defined($paramdesc)) {
  1716. push @final_params, $_;
  1717. push @final_params, undef;
  1718. push @final_params, $paramdesc;
  1719. }
  1720. }
  1721. }
  1722. my $hfiletext = $wikiheaderfiletext;
  1723. $hfiletext =~ s/\%fname\%/$headersymslocation{$sym}/g;
  1724. $sections{'Header File'} = "$hfiletext\n";
  1725. # Make sure this ends with a double-newline.
  1726. $sections{'See Also'} .= "\n" if defined $sections{'See Also'};
  1727. if (0) { # !!! FIXME: this was a useful hack, but this needs to be generalized if we're going to do this always.
  1728. # Plug in a \since section if one wasn't listed.
  1729. if (not defined $sections{'Version'}) {
  1730. my $symtypename;
  1731. if ($symtype == 1) {
  1732. $symtypename = 'function';
  1733. } elsif ($symtype == 2) {
  1734. $symtypename = 'macro';
  1735. } elsif ($symtype == 3) {
  1736. $symtypename = 'struct';
  1737. } elsif ($symtype == 4) {
  1738. $symtypename = 'enum';
  1739. } elsif ($symtype == 5) {
  1740. $symtypename = 'datatype';
  1741. } else {
  1742. die("Unexpected symbol type $symtype!");
  1743. }
  1744. my $str = "This $symtypename is available since SDL 3.0.0.";
  1745. $sections{'Version'} = wordwrap(wikify($wikitype, $str)) . "\n";
  1746. }
  1747. }
  1748. # We can build the wiki table now that we have all the data.
  1749. if (scalar(@final_params) > 0) {
  1750. my $str = '';
  1751. if ($wikitype eq 'mediawiki') {
  1752. while (scalar(@final_params) > 0) {
  1753. my $arg = shift @final_params;
  1754. my $c_datatype = shift @final_params;
  1755. my $desc = wikify($wikitype, shift @final_params);
  1756. $c_datatype = '' if not defined $c_datatype;
  1757. $str .= ($str eq '') ? "{|\n" : "|-\n";
  1758. $str .= "|$c_datatype\n" if $params_has_c_datatype;
  1759. $str .= "|'''$arg'''\n";
  1760. $str .= "|$desc\n";
  1761. }
  1762. $str .= "|}\n";
  1763. } elsif ($wikitype eq 'md') {
  1764. my $longest_arg = 0;
  1765. my $longest_c_datatype = 0;
  1766. my $longest_desc = 0;
  1767. my $which = 0;
  1768. foreach (@final_params) {
  1769. if ($which == 0) {
  1770. my $len = length($_);
  1771. $longest_arg = $len if ($len > $longest_arg);
  1772. $which = 1;
  1773. } elsif ($which == 1) {
  1774. if (defined($_)) {
  1775. my $len = length(wikify($wikitype, $_));
  1776. $longest_c_datatype = $len if ($len > $longest_c_datatype);
  1777. }
  1778. $which = 2;
  1779. } else {
  1780. my $len = length(wikify($wikitype, $_));
  1781. $longest_desc = $len if ($len > $longest_desc);
  1782. $which = 0;
  1783. }
  1784. }
  1785. # Markdown tables are sort of obnoxious.
  1786. my $c_datatype_cell;
  1787. $c_datatype_cell = ($longest_c_datatype > 0) ? ('| ' . (' ' x ($longest_c_datatype)) . ' ') : '';
  1788. $str .= $c_datatype_cell . '| ' . (' ' x ($longest_arg+4)) . ' | ' . (' ' x $longest_desc) . " |\n";
  1789. $c_datatype_cell = ($longest_c_datatype > 0) ? ('| ' . ('-' x ($longest_c_datatype)) . ' ') : '';
  1790. $str .= $c_datatype_cell . '| ' . ('-' x ($longest_arg+4)) . ' | ' . ('-' x $longest_desc) . " |\n";
  1791. while (@final_params) {
  1792. my $arg = shift @final_params;
  1793. my $c_datatype = shift @final_params;
  1794. $c_datatype_cell = '';
  1795. if ($params_has_c_datatype) {
  1796. $c_datatype = defined($c_datatype) ? wikify($wikitype, $c_datatype) : '';
  1797. $c_datatype_cell = ($longest_c_datatype > 0) ? ("| $c_datatype " . (' ' x ($longest_c_datatype - length($c_datatype)))) : '';
  1798. }
  1799. my $desc = wikify($wikitype, shift @final_params);
  1800. $str .= $c_datatype_cell . "| **$arg** " . (' ' x ($longest_arg - length($arg))) . "| $desc" . (' ' x ($longest_desc - length($desc))) . " |\n";
  1801. }
  1802. } else {
  1803. die("Unexpected wikitype!"); # should have checked this elsewhere.
  1804. }
  1805. $sections{'Function Parameters'} = $str;
  1806. }
  1807. my $path = "$wikipath/$sym.${wikitype}.tmp";
  1808. open(FH, '>', $path) or die("Can't open '$path': $!\n");
  1809. my $sectionsref = $wikisyms{$sym};
  1810. foreach (@standard_wiki_sections) {
  1811. # drop sections we either replaced or removed from the original wiki's contents.
  1812. if (not defined $only_wiki_sections{$_}) {
  1813. delete($$sectionsref{$_});
  1814. }
  1815. }
  1816. my $wikisectionorderref = $wikisectionorder{$sym};
  1817. # Make sure there's a footer in the wiki that puts this function in CategoryAPI...
  1818. if (not $$sectionsref{'[footer]'}) {
  1819. $$sectionsref{'[footer]'} = '';
  1820. push @$wikisectionorderref, '[footer]';
  1821. }
  1822. # If changing format, convert things that otherwise are passed through unmolested.
  1823. if (defined $changeformat) {
  1824. if (($dewikify_mode eq 'md') and ($origwikitype eq 'mediawiki')) {
  1825. $$sectionsref{'[footer]'} =~ s/\[\[(Category[a-zA-Z0-9_]+)\]\]/[$1]($1)/g;
  1826. } elsif (($dewikify_mode eq 'mediawiki') and ($origwikitype eq 'md')) {
  1827. $$sectionsref{'[footer]'} =~ s/\[(Category[a-zA-Z0-9_]+)\]\(.*?\)/[[$1]]/g;
  1828. }
  1829. foreach (keys %only_wiki_sections) {
  1830. my $sect = $_;
  1831. if (defined $$sectionsref{$sect}) {
  1832. $$sectionsref{$sect} = wikify($wikitype, dewikify($origwikitype, $$sectionsref{$sect}));
  1833. }
  1834. }
  1835. }
  1836. if ($symtype != -1) { # Don't do these in category documentation block
  1837. my $footer = $$sectionsref{'[footer]'};
  1838. my $symtypename;
  1839. if ($symtype == 1) {
  1840. $symtypename = 'Function';
  1841. } elsif ($symtype == 2) {
  1842. $symtypename = 'Macro';
  1843. } elsif ($symtype == 3) {
  1844. $symtypename = 'Struct';
  1845. } elsif ($symtype == 4) {
  1846. $symtypename = 'Enum';
  1847. } elsif ($symtype == 5) {
  1848. $symtypename = 'Datatype';
  1849. } else {
  1850. die("Unexpected symbol type $symtype!");
  1851. }
  1852. my $symcategory = $headersymscategory{$sym};
  1853. if ($wikitype eq 'mediawiki') {
  1854. $footer =~ s/\[\[CategoryAPI\]\],?\s*//g;
  1855. $footer =~ s/\[\[CategoryAPI${symtypename}\]\],?\s*//g;
  1856. $footer =~ s/\[\[Category${symcategory}\]\],?\s*//g if defined $symcategory;
  1857. $footer = "[[CategoryAPI]], [[CategoryAPI$symtypename]]" . (defined $symcategory ? ", [[Category$symcategory]]" : '') . (($footer eq '') ? "\n" : ", $footer");
  1858. } elsif ($wikitype eq 'md') {
  1859. $footer =~ s/\[CategoryAPI\]\(CategoryAPI\),?\s*//g;
  1860. $footer =~ s/\[CategoryAPI${symtypename}\]\(CategoryAPI${symtypename}\),?\s*//g;
  1861. $footer =~ s/\[Category${symcategory}\]\(Category${symcategory}\),?\s*//g if defined $symcategory;
  1862. $footer = "[CategoryAPI](CategoryAPI), [CategoryAPI$symtypename](CategoryAPI$symtypename)" . (defined $symcategory ? ", [Category$symcategory](Category$symcategory)" : '') . (($footer eq '') ? '' : ', ') . $footer;
  1863. } else { die("Unexpected wikitype '$wikitype'"); }
  1864. $$sectionsref{'[footer]'} = $footer;
  1865. if (defined $wikipreamble) {
  1866. my $wikified_preamble = wikify($wikitype, $wikipreamble);
  1867. if ($wikitype eq 'mediawiki') {
  1868. print FH "====== $wikified_preamble ======\n";
  1869. } elsif ($wikitype eq 'md') {
  1870. print FH "###### $wikified_preamble\n";
  1871. } else { die("Unexpected wikitype '$wikitype'"); }
  1872. }
  1873. }
  1874. my $prevsectstr = '';
  1875. my @ordered_sections = (@standard_wiki_sections, defined $wikisectionorderref ? @$wikisectionorderref : ()); # this copies the arrays into one.
  1876. foreach (@ordered_sections) {
  1877. my $sect = $_;
  1878. next if $sect eq '[start]';
  1879. next if (not defined $sections{$sect} and not defined $$sectionsref{$sect});
  1880. my $section = defined $sections{$sect} ? $sections{$sect} : $$sectionsref{$sect};
  1881. if ($sect eq '[footer]') {
  1882. # Make sure previous section ends with two newlines.
  1883. if (substr($prevsectstr, -1) ne "\n") {
  1884. print FH "\n\n";
  1885. } elsif (substr($prevsectstr, -2) ne "\n\n") {
  1886. print FH "\n";
  1887. }
  1888. print FH "----\n"; # It's the same in Markdown and MediaWiki.
  1889. } elsif ($sect eq '[Brief]') {
  1890. if ($wikitype eq 'mediawiki') {
  1891. print FH "= $sym =\n\n";
  1892. } elsif ($wikitype eq 'md') {
  1893. print FH "# $sym\n\n";
  1894. } else { die("Unexpected wikitype '$wikitype'"); }
  1895. } else {
  1896. my $sectname = $sect;
  1897. if ($sectname eq 'Function Parameters') { # We use this same table for different things depending on what we're documenting, so rename it now.
  1898. if (($symtype == 1) || ($symtype == 5)) { # function (or typedef, in case it's a function pointer type).
  1899. } elsif ($symtype == 2) { # macro
  1900. $sectname = 'Macro Parameters';
  1901. } elsif ($symtype == 3) { # struct/union
  1902. $sectname = 'Fields';
  1903. } elsif ($symtype == 4) { # enum
  1904. $sectname = 'Values';
  1905. } else {
  1906. die("Unexpected symtype $symtype");
  1907. }
  1908. }
  1909. if ($symtype != -1) { # Not for category documentation block
  1910. if ($wikitype eq 'mediawiki') {
  1911. print FH "\n== $sectname ==\n\n";
  1912. } elsif ($wikitype eq 'md') {
  1913. print FH "\n## $sectname\n\n";
  1914. } else { die("Unexpected wikitype '$wikitype'"); }
  1915. }
  1916. }
  1917. my $sectstr = defined $sections{$sect} ? $sections{$sect} : $$sectionsref{$sect};
  1918. print FH $sectstr;
  1919. $prevsectstr = $sectstr;
  1920. # make sure these don't show up twice.
  1921. delete($sections{$sect});
  1922. delete($$sectionsref{$sect});
  1923. }
  1924. print FH "\n\n";
  1925. close(FH);
  1926. if (defined $changeformat and ($origwikitype ne $wikitype)) {
  1927. system("cd '$wikipath' ; git mv '$_.${origwikitype}' '$_.${wikitype}'");
  1928. unlink("$wikipath/$_.${origwikitype}");
  1929. }
  1930. rename($path, "$wikipath/$_.${wikitype}") or die("Can't rename '$path' to '$wikipath/$_.${wikitype}': $!\n");
  1931. }
  1932. # Write out simple redirector pages if they don't already exist.
  1933. foreach (keys %referenceonly) {
  1934. my $sym = $_;
  1935. my $refersto = $referenceonly{$sym};
  1936. my $path = "$wikipath/$sym.md"; # we only do Markdown for these.
  1937. next if (-f $path); # don't overwrite if it already exists. Delete the file if you need a rebuild!
  1938. open(FH, '>', $path) or die("Can't open '$path': $!\n");
  1939. if (defined $wikipreamble) {
  1940. my $wikified_preamble = wikify('md', $wikipreamble);
  1941. print FH "###### $wikified_preamble\n";
  1942. }
  1943. print FH "# $sym\n\nPlease refer to [$refersto]($refersto) for details.\n\n";
  1944. print FH "----\n";
  1945. print FH "[CategoryAPI](CategoryAPI), [CategoryAPIMacro](CategoryAPIMacro)\n\n";
  1946. close(FH);
  1947. }
  1948. # Write out Category pages...
  1949. foreach (keys %headercategorydocs) {
  1950. my $cat = $_;
  1951. my $sym = $headercategorydocs{$cat}; # fake symbol
  1952. my $raw = $headersyms{$sym}; # raw doxygen text with comment characters stripped from start/end and start of each line.
  1953. my $wikitype = defined($wikitypes{$sym}) ? $wikitypes{$sym} : 'md';
  1954. my $path = "$wikipath/Category$cat.$wikitype";
  1955. $raw = wordwrap(wikify($wikitype, $raw));
  1956. my $tmppath = "$path.tmp";
  1957. open(FH, '>', $tmppath) or die("Can't open '$tmppath': $!\n");
  1958. print FH "$raw\n\n";
  1959. if (! -f $path) { # Doesn't exist at all? Write out a template file.
  1960. # If writing from scratch, it's always a Markdown file.
  1961. die("Unexpected wikitype '$wikitype'!") if $wikitype ne 'md';
  1962. print FH <<__EOF__
  1963. <!-- END CATEGORY DOCUMENTATION -->
  1964. ## Functions
  1965. <!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. -->
  1966. <!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIFunction -->
  1967. <!-- END CATEGORY LIST -->
  1968. ## Datatypes
  1969. <!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. -->
  1970. <!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIDatatype -->
  1971. <!-- END CATEGORY LIST -->
  1972. ## Structs
  1973. <!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. -->
  1974. <!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIStruct -->
  1975. <!-- END CATEGORY LIST -->
  1976. ## Enums
  1977. <!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. -->
  1978. <!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIEnum -->
  1979. <!-- END CATEGORY LIST -->
  1980. ## Macros
  1981. <!-- DO NOT HAND-EDIT CATEGORY LISTS, THEY ARE AUTOGENERATED AND WILL BE OVERWRITTEN, BASED ON TAGS IN INDIVIDUAL PAGE FOOTERS. EDIT THOSE INSTEAD. -->
  1982. <!-- BEGIN CATEGORY LIST: Category$cat, CategoryAPIMacro -->
  1983. <!-- END CATEGORY LIST -->
  1984. ----
  1985. [CategoryAPICategory](CategoryAPICategory)
  1986. __EOF__
  1987. ;
  1988. } else {
  1989. my $endstr = $wikisyms{$sym}->{'[footer]'};
  1990. if (defined($endstr)) {
  1991. print FH $endstr;
  1992. }
  1993. }
  1994. close(FH);
  1995. rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n");
  1996. }
  1997. # Write out READMEs...
  1998. if (defined $readmepath) {
  1999. if ( -d $readmepath ) {
  2000. mkdir($wikireadmepath); # just in case
  2001. opendir(DH, $readmepath) or die("Can't opendir '$readmepath': $!\n");
  2002. while (my $d = readdir(DH)) {
  2003. my $dent = $d;
  2004. if ($dent =~ /\AREADME\-(.*?\.md)\Z/) { # we only bridge Markdown files here.
  2005. my $wikifname = $1;
  2006. next if $wikifname eq 'FrontPage.md';
  2007. filecopy("$readmepath/$dent", "$wikireadmepath/$wikifname", "\n");
  2008. }
  2009. }
  2010. closedir(DH);
  2011. my @pages = ();
  2012. opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n");
  2013. while (my $d = readdir(DH)) {
  2014. my $dent = $d;
  2015. if ($dent =~ /\A(.*?)\.(mediawiki|md)\Z/) {
  2016. my $wikiname = $1;
  2017. next if $wikiname eq 'FrontPage';
  2018. push @pages, $wikiname;
  2019. }
  2020. }
  2021. closedir(DH);
  2022. open(FH, '>', "$wikireadmepath/FrontPage.md") or die("Can't open '$wikireadmepath/FrontPage.md': $!\n");
  2023. print FH "# All READMEs available here\n\n";
  2024. foreach (sort @pages) {
  2025. my $wikiname = $_;
  2026. print FH "- [$wikiname]($wikiname)\n";
  2027. }
  2028. close(FH);
  2029. }
  2030. }
  2031. } elsif ($copy_direction == -2) { # --copy-to-manpages
  2032. # This only takes from the wiki data, since it has sections we omit from the headers, like code examples.
  2033. File::Path::make_path("$manpath/man3");
  2034. $dewikify_mode = 'manpage';
  2035. $wordwrap_mode = 'manpage';
  2036. my $introtxt = '';
  2037. if (0) {
  2038. open(FH, '<', "$srcpath/LICENSE.txt") or die("Can't open '$srcpath/LICENSE.txt': $!\n");
  2039. while (<FH>) {
  2040. chomp;
  2041. $introtxt .= ".\\\" $_\n";
  2042. }
  2043. close(FH);
  2044. }
  2045. if (!$gitrev) {
  2046. $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`;
  2047. chomp($gitrev);
  2048. }
  2049. # !!! FIXME
  2050. open(FH, '<', "$srcpath/$versionfname") or die("Can't open '$srcpath/$versionfname': $!\n");
  2051. my $majorver = 0;
  2052. my $minorver = 0;
  2053. my $microver = 0;
  2054. while (<FH>) {
  2055. chomp;
  2056. if (/$versionmajorregex/) {
  2057. $majorver = int($1);
  2058. } elsif (/$versionminorregex/) {
  2059. $minorver = int($1);
  2060. } elsif (/$versionmicroregex/) {
  2061. $microver = int($1);
  2062. }
  2063. }
  2064. close(FH);
  2065. my $fullversion = "$majorver.$minorver.$microver";
  2066. foreach (keys %headersyms) {
  2067. my $sym = $_;
  2068. next if not defined $wikisyms{$sym}; # don't have a page for that function, skip it.
  2069. next if $sym =~ /\A\[category documentation\]/; # not real symbols
  2070. my $symtype = $headersymstype{$sym};
  2071. my $wikitype = $wikitypes{$sym};
  2072. my $sectionsref = $wikisyms{$sym};
  2073. my $remarks = $sectionsref->{'Remarks'};
  2074. my $params = $sectionsref->{'Function Parameters'};
  2075. my $returns = $sectionsref->{'Return Value'};
  2076. my $version = $sectionsref->{'Version'};
  2077. my $threadsafety = $sectionsref->{'Thread Safety'};
  2078. my $related = $sectionsref->{'See Also'};
  2079. my $examples = $sectionsref->{'Code Examples'};
  2080. my $deprecated = $sectionsref->{'Deprecated'};
  2081. my $headerfile = $manpageheaderfiletext;
  2082. $headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g;
  2083. $headerfile .= "\n";
  2084. my $mansection;
  2085. my $mansectionname;
  2086. if (($symtype == 1) || ($symtype == 2)) { # functions or macros
  2087. $mansection = '3';
  2088. $mansectionname = 'FUNCTIONS';
  2089. } elsif (($symtype >= 3) && ($symtype <= 5)) { # struct/union/enum/typedef
  2090. $mansection = '3type';
  2091. $mansectionname = 'DATATYPES';
  2092. } else {
  2093. die("Unexpected symtype $symtype");
  2094. }
  2095. my $brief = $sectionsref->{'[Brief]'};
  2096. my $decl = $headerdecls{$sym};
  2097. my $str = '';
  2098. $brief = "$brief";
  2099. $brief =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms;
  2100. $brief =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms;
  2101. $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  2102. my @briefsplit = split /\n/, $brief;
  2103. $brief = shift @briefsplit;
  2104. $brief = dewikify($wikitype, $brief);
  2105. if (defined $remarks) {
  2106. $remarks = dewikify($wikitype, join("\n", @briefsplit) . $remarks);
  2107. }
  2108. $str .= $introtxt;
  2109. $str .= ".\\\" This manpage content is licensed under Creative Commons\n";
  2110. $str .= ".\\\" Attribution 4.0 International (CC BY 4.0)\n";
  2111. $str .= ".\\\" https://creativecommons.org/licenses/by/4.0/\n";
  2112. $str .= ".\\\" This manpage was generated from ${projectshortname}'s wiki page for $sym:\n";
  2113. $str .= ".\\\" $wikiurl/$sym\n";
  2114. $str .= ".\\\" Generated with SDL/build-scripts/wikiheaders.pl\n";
  2115. $str .= ".\\\" revision $gitrev\n" if $gitrev ne '';
  2116. $str .= ".\\\" Please report issues in this manpage's content at:\n";
  2117. $str .= ".\\\" $bugreporturl\n";
  2118. $str .= ".\\\" Please report issues in the generation of this manpage from the wiki at:\n";
  2119. $str .= ".\\\" https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n";
  2120. $str .= ".\\\" $projectshortname can be found at $projecturl\n";
  2121. # 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.
  2122. # This wizadry is from https://web.archive.org/web/20060102165607/http://people.debian.org/~branden/talks/wtfm/wtfm.pdf
  2123. $str .= ".de URL\n";
  2124. $str .= '\\$2 \(laURL: \\$1 \(ra\\$3' . "\n";
  2125. $str .= "..\n";
  2126. $str .= '.if \n[.g] .mso www.tmac' . "\n";
  2127. $str .= ".TH $sym $mansection \"$projectshortname $fullversion\" \"$projectfullname\" \"$projectshortname$majorver $mansectionname\"\n";
  2128. $str .= ".SH NAME\n";
  2129. $str .= "$sym";
  2130. $str .= " \\- $brief" if (defined $brief);
  2131. $str .= "\n";
  2132. if (defined $deprecated) {
  2133. $str .= ".SH DEPRECATED\n";
  2134. $str .= dewikify($wikitype, $deprecated) . "\n";
  2135. }
  2136. if (defined $headerfile) {
  2137. $str .= ".SH HEADER FILE\n";
  2138. $str .= dewikify($wikitype, $headerfile) . "\n";
  2139. }
  2140. $str .= ".SH SYNOPSIS\n";
  2141. $str .= ".nf\n";
  2142. $str .= ".B #include \\(dq$mainincludefname\\(dq\n";
  2143. $str .= ".PP\n";
  2144. my @decllines = split /\n/, $decl;
  2145. foreach (@decllines) {
  2146. $str .= ".BI \"$_\n";
  2147. }
  2148. $str .= ".fi\n";
  2149. if (defined $remarks) {
  2150. $str .= ".SH DESCRIPTION\n";
  2151. $str .= $remarks . "\n";
  2152. }
  2153. if (defined $params) {
  2154. if (($symtype == 1) || ($symtype == 5)) {
  2155. $str .= ".SH FUNCTION PARAMETERS\n";
  2156. } elsif ($symtype == 2) { # macro
  2157. $str .= ".SH MACRO PARAMETERS\n";
  2158. } elsif ($symtype == 3) { # struct/union
  2159. $str .= ".SH FIELDS\n";
  2160. } elsif ($symtype == 4) { # enum
  2161. $str .= ".SH VALUES\n";
  2162. } else {
  2163. die("Unexpected symtype $symtype");
  2164. }
  2165. my @lines = split /\n/, $params;
  2166. if ($wikitype eq 'mediawiki') {
  2167. die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start
  2168. while (scalar(@lines) >= 3) {
  2169. my $c_datatype = shift @lines;
  2170. my $name = shift @lines;
  2171. my $desc = shift @lines;
  2172. my $terminator; # the '|-' or '|}' line.
  2173. if (($desc eq '|-') or ($desc eq '|}') or (not $desc =~ /\A\|/)) { # we seem to be out of cells, which means there was no datatype column on this one.
  2174. $terminator = $desc;
  2175. $desc = $name;
  2176. $name = $c_datatype;
  2177. $c_datatype = '';
  2178. } else {
  2179. $terminator = shift @lines;
  2180. }
  2181. last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table.
  2182. $name =~ s/\A\|\s*//;
  2183. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  2184. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  2185. $desc =~ s/\A\|\s*//;
  2186. $desc = dewikify($wikitype, $desc);
  2187. #print STDERR "SYM: $sym CDATATYPE: $c_datatype NAME: $name DESC: $desc TERM: $terminator\n";
  2188. $str .= ".TP\n";
  2189. $str .= ".I $name\n";
  2190. $str .= "$desc\n";
  2191. }
  2192. } elsif ($wikitype eq 'md') {
  2193. my $l;
  2194. $l = shift @lines;
  2195. die("Unexpected data parsing Markdown table") if (not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/);
  2196. $l = shift @lines;
  2197. die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/);
  2198. while (scalar(@lines) >= 1) {
  2199. $l = shift @lines;
  2200. my $name;
  2201. my $desc;
  2202. if ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) {
  2203. # c datatype is $1, but we don't care about it here.
  2204. $name = $2;
  2205. $desc = $3;
  2206. } elsif ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) {
  2207. $name = $1;
  2208. $desc = $2;
  2209. } else {
  2210. last; # we seem to have run out of table.
  2211. }
  2212. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  2213. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  2214. $desc = dewikify($wikitype, $desc);
  2215. $str .= ".TP\n";
  2216. $str .= ".I $name\n";
  2217. $str .= "$desc\n";
  2218. }
  2219. } else {
  2220. die("write me");
  2221. }
  2222. }
  2223. if (defined $returns) {
  2224. $returns = dewikify($wikitype, $returns);
  2225. $returns =~ s/\A\(.*?\)\s*//; # Chop datatype in parentheses off the front.
  2226. $str .= ".SH RETURN VALUE\n";
  2227. $str .= "$returns\n";
  2228. }
  2229. if (defined $examples) {
  2230. $str .= ".SH CODE EXAMPLES\n";
  2231. $dewikify_manpage_code_indent = 0;
  2232. $str .= dewikify($wikitype, $examples) . "\n";
  2233. $dewikify_manpage_code_indent = 1;
  2234. }
  2235. if (defined $threadsafety) {
  2236. $str .= ".SH THREAD SAFETY\n";
  2237. $str .= dewikify($wikitype, $threadsafety) . "\n";
  2238. }
  2239. if (defined $version) {
  2240. $str .= ".SH AVAILABILITY\n";
  2241. $str .= dewikify($wikitype, $version) . "\n";
  2242. }
  2243. if (defined $related) {
  2244. $str .= ".SH SEE ALSO\n";
  2245. # !!! FIXME: lots of code duplication in all of these.
  2246. my $v = dewikify($wikitype, $related);
  2247. my @desclines = split /\n/, $v;
  2248. my $nextstr = '';
  2249. foreach (@desclines) {
  2250. s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func"
  2251. s/\[\[(.*?)\]\]/$1/; # in case some wikilinks remain.
  2252. s/\[(.*?)\]\(.*?\)/$1/; # in case some wikilinks remain.
  2253. s/\A\*\s*\Z//;
  2254. s/\A\/*//;
  2255. s/\A\.BR\s+//; # dewikify added this, but we want to handle it.
  2256. s/\A\.I\s+//; # dewikify added this, but we want to handle it.
  2257. s/\A\s*[\:\*\-]\s*//;
  2258. s/\A\s+//;
  2259. s/\s+\Z//;
  2260. next if $_ eq '';
  2261. my $seealso_symtype = $headersymstype{$_};
  2262. my $seealso_mansection = '3';
  2263. if (defined($seealso_symtype) && ($seealso_symtype >= 3) && ($seealso_symtype <= 5)) { # struct/union/enum/typedef
  2264. $seealso_mansection = '3type';
  2265. }
  2266. $str .= "$nextstr.BR $_ ($seealso_mansection)";
  2267. $nextstr = ",\n";
  2268. }
  2269. $str .= "\n";
  2270. }
  2271. if (0) {
  2272. $str .= ".SH COPYRIGHT\n";
  2273. $str .= "This manpage is licensed under\n";
  2274. $str .= ".UR https://creativecommons.org/licenses/by/4.0/\n";
  2275. $str .= "Creative Commons Attribution 4.0 International (CC BY 4.0)\n";
  2276. $str .= ".UE\n";
  2277. $str .= ".PP\n";
  2278. $str .= "This manpage was generated from\n";
  2279. $str .= ".UR $wikiurl/$sym\n";
  2280. $str .= "${projectshortname}'s wiki\n";
  2281. $str .= ".UE\n";
  2282. $str .= "using SDL/build-scripts/wikiheaders.pl";
  2283. $str .= " revision $gitrev" if $gitrev ne '';
  2284. $str .= ".\n";
  2285. $str .= "Please report issues in this manpage at\n";
  2286. $str .= ".UR $bugreporturl\n";
  2287. $str .= "our bugtracker!\n";
  2288. $str .= ".UE\n";
  2289. }
  2290. my $path = "$manpath/man3/$_.$mansection";
  2291. my $tmppath = "$path.tmp";
  2292. open(FH, '>', $tmppath) or die("Can't open '$tmppath': $!\n");
  2293. print FH $str;
  2294. close(FH);
  2295. rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n");
  2296. }
  2297. } elsif ($copy_direction == -4) { # --copy-to-latex
  2298. # This only takes from the wiki data, since it has sections we omit from the headers, like code examples.
  2299. print STDERR "\n(The --copy-to-latex code is known to not be ready for serious use; send patches, not bug reports, please.)\n\n";
  2300. $dewikify_mode = 'LaTeX';
  2301. $wordwrap_mode = 'LaTeX';
  2302. # !!! FIXME: code duplication with --copy-to-manpages section.
  2303. my $introtxt = '';
  2304. if (0) {
  2305. open(FH, '<', "$srcpath/LICENSE.txt") or die("Can't open '$srcpath/LICENSE.txt': $!\n");
  2306. while (<FH>) {
  2307. chomp;
  2308. $introtxt .= ".\\\" $_\n";
  2309. }
  2310. close(FH);
  2311. }
  2312. if (!$gitrev) {
  2313. $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`;
  2314. chomp($gitrev);
  2315. }
  2316. # !!! FIXME
  2317. open(FH, '<', "$srcpath/$versionfname") or die("Can't open '$srcpath/$versionfname': $!\n");
  2318. my $majorver = 0;
  2319. my $minorver = 0;
  2320. my $microver = 0;
  2321. while (<FH>) {
  2322. chomp;
  2323. if (/$versionmajorregex/) {
  2324. $majorver = int($1);
  2325. } elsif (/$versionminorregex/) {
  2326. $minorver = int($1);
  2327. } elsif (/$versionmicroregex/) {
  2328. $microver = int($1);
  2329. }
  2330. }
  2331. close(FH);
  2332. my $fullversion = "$majorver.$minorver.$microver";
  2333. my $latex_fname = "$srcpath/$projectshortname.tex";
  2334. my $latex_tmpfname = "$latex_fname.tmp";
  2335. open(TEXFH, '>', "$latex_tmpfname") or die("Can't open '$latex_tmpfname' for writing: $!\n");
  2336. print TEXFH <<__EOF__
  2337. \\documentclass{book}
  2338. \\usepackage{listings}
  2339. \\usepackage{color}
  2340. \\usepackage{hyperref}
  2341. \\definecolor{dkgreen}{rgb}{0,0.6,0}
  2342. \\definecolor{gray}{rgb}{0.5,0.5,0.5}
  2343. \\definecolor{mauve}{rgb}{0.58,0,0.82}
  2344. \\setcounter{secnumdepth}{0}
  2345. \\lstset{frame=tb,
  2346. language=C,
  2347. aboveskip=3mm,
  2348. belowskip=3mm,
  2349. showstringspaces=false,
  2350. columns=flexible,
  2351. basicstyle={\\small\\ttfamily},
  2352. numbers=none,
  2353. numberstyle=\\tiny\\color{gray},
  2354. keywordstyle=\\color{blue},
  2355. commentstyle=\\color{dkgreen},
  2356. stringstyle=\\color{mauve},
  2357. breaklines=true,
  2358. breakatwhitespace=true,
  2359. tabsize=3
  2360. }
  2361. \\begin{document}
  2362. \\frontmatter
  2363. \\title{$projectfullname $majorver.$minorver.$microver Reference Manual}
  2364. \\author{The $projectshortname Developers}
  2365. \\maketitle
  2366. \\mainmatter
  2367. __EOF__
  2368. ;
  2369. # !!! FIXME: Maybe put this in the book intro? print TEXFH $introtxt;
  2370. # Sort symbols by symbol type, then alphabetically.
  2371. my @headersymskeys = sort {
  2372. my $symtypea = $headersymstype{$a};
  2373. my $symtypeb = $headersymstype{$b};
  2374. $symtypea = 3 if ($symtypea > 3);
  2375. $symtypeb = 3 if ($symtypeb > 3);
  2376. my $rc = $symtypea <=> $symtypeb;
  2377. if ($rc == 0) {
  2378. $rc = lc($a) cmp lc($b);
  2379. }
  2380. return $rc;
  2381. } keys %headersyms;
  2382. my $current_symtype = 0;
  2383. my $current_chapter = '';
  2384. foreach (@headersymskeys) {
  2385. my $sym = $_;
  2386. next if not defined $wikisyms{$sym}; # don't have a page for that function, skip it.
  2387. next if $sym =~ /\A\[category documentation\]/; # not real symbols.
  2388. my $symtype = $headersymstype{$sym};
  2389. my $wikitype = $wikitypes{$sym};
  2390. my $sectionsref = $wikisyms{$sym};
  2391. my $remarks = $sectionsref->{'Remarks'};
  2392. my $params = $sectionsref->{'Function Parameters'};
  2393. my $returns = $sectionsref->{'Return Value'};
  2394. my $version = $sectionsref->{'Version'};
  2395. my $threadsafety = $sectionsref->{'Thread Safety'};
  2396. my $related = $sectionsref->{'See Also'};
  2397. my $examples = $sectionsref->{'Code Examples'};
  2398. my $deprecated = $sectionsref->{'Deprecated'};
  2399. my $headerfile = $manpageheaderfiletext;
  2400. $headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g;
  2401. $headerfile .= "\n";
  2402. my $brief = $sectionsref->{'[Brief]'};
  2403. my $decl = $headerdecls{$sym};
  2404. my $str = '';
  2405. if ($current_symtype != $symtype) {
  2406. my $newchapter = '';
  2407. if ($symtype == 1) {
  2408. $newchapter = 'Functions';
  2409. } elsif ($symtype == 2) {
  2410. $newchapter = 'Macros';
  2411. } else {
  2412. $newchapter = 'Datatypes';
  2413. }
  2414. if ($current_chapter ne $newchapter) {
  2415. $str .= "\n\n\\chapter{$projectshortname $newchapter}\n\n\\clearpage\n\n";
  2416. $current_chapter = $newchapter;
  2417. }
  2418. $current_symtype = $symtype;
  2419. }
  2420. $brief = "$brief";
  2421. $brief =~ s/\A[\s\n]*\= .*? \=\s*?\n+//ms;
  2422. $brief =~ s/\A[\s\n]*\=\= .*? \=\=\s*?\n+//ms;
  2423. $brief =~ s/\A(.*?\.) /$1\n/; # \brief should only be one sentence, delimited by a period+space. Split if necessary.
  2424. my @briefsplit = split /\n/, $brief;
  2425. $brief = shift @briefsplit;
  2426. $brief = dewikify($wikitype, $brief);
  2427. if (defined $remarks) {
  2428. $remarks = dewikify($wikitype, join("\n", @briefsplit) . $remarks);
  2429. }
  2430. my $escapedsym = escLaTeX($sym);
  2431. $str .= "\\hypertarget{$sym}{%\n\\section{$escapedsym}\\label{$sym}}\n\n";
  2432. $str .= $brief if (defined $brief);
  2433. $str .= "\n\n";
  2434. if (defined $deprecated) {
  2435. $str .= "\\subsection{Deprecated}\n\n";
  2436. $str .= dewikify($wikitype, $deprecated) . "\n";
  2437. }
  2438. if (defined $headerfile) {
  2439. $str .= "\\subsection{Header File}\n\n";
  2440. $str .= dewikify($wikitype, $headerfile) . "\n";
  2441. }
  2442. $str .= "\\subsection{Syntax}\n\n";
  2443. $str .= "\\begin{lstlisting}\n$decl\n\\end{lstlisting}\n";
  2444. if (defined $params) {
  2445. if (($symtype == 1) || ($symtype == 5)) {
  2446. $str .= "\\subsection{Function Parameters}\n\n";
  2447. } elsif ($symtype == 2) { # macro
  2448. $str .= "\\subsection{Macro Parameters}\n\n";
  2449. } elsif ($symtype == 3) { # struct/union
  2450. $str .= "\\subsection{Fields}\n\n";
  2451. } elsif ($symtype == 4) { # enum
  2452. $str .= "\\subsection{Values}\n\n";
  2453. } else {
  2454. die("Unexpected symtype $symtype");
  2455. }
  2456. $str .= "\\begin{center}\n";
  2457. $str .= " \\begin{tabular}{ | l | p{0.75\\textwidth} |}\n";
  2458. $str .= " \\hline\n";
  2459. # !!! FIXME: this table parsing has gotten complicated and is pasted three times in this file; move it to a subroutine!
  2460. my @lines = split /\n/, $params;
  2461. if ($wikitype eq 'mediawiki') {
  2462. die("Unexpected data parsing MediaWiki table") if (shift @lines ne '{|'); # Dump the '{|' start
  2463. while (scalar(@lines) >= 3) {
  2464. my $name = shift @lines;
  2465. my $desc = shift @lines;
  2466. my $terminator = shift @lines; # the '|-' or '|}' line.
  2467. last if ($terminator ne '|-') and ($terminator ne '|}'); # we seem to have run out of table.
  2468. $name =~ s/\A\|\s*//;
  2469. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  2470. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  2471. $name = escLaTeX($name);
  2472. $desc =~ s/\A\|\s*//;
  2473. $desc = dewikify($wikitype, $desc);
  2474. #print STDERR "FN: $sym NAME: $name DESC: $desc TERM: $terminator\n";
  2475. $str .= " \\textbf{$name} & $desc \\\\ \\hline\n";
  2476. }
  2477. } elsif ($wikitype eq 'md') {
  2478. my $l;
  2479. $l = shift @lines;
  2480. die("Unexpected data parsing Markdown table") if (not $l =~ /\A(\s*\|)?\s*\|\s*\|\s*\|\s*\Z/);
  2481. $l = shift @lines;
  2482. die("Unexpected data parsing Markdown table") if (not $l =~ /\A\s*(\|\s*\-*\s*)?\|\s*\-*\s*\|\s*\-*\s*\|\s*\Z/);
  2483. while (scalar(@lines) >= 1) {
  2484. $l = shift @lines;
  2485. my $name;
  2486. my $desc;
  2487. if ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) {
  2488. # c datatype is $1, but we don't care about it here.
  2489. $name = $2;
  2490. $desc = $3;
  2491. } elsif ($l =~ /\A\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|\s*\Z/) {
  2492. $name = $1;
  2493. $desc = $2;
  2494. } else {
  2495. last; # we seem to have run out of table.
  2496. }
  2497. $name =~ s/\A\*\*(.*?)\*\*/$1/;
  2498. $name =~ s/\A\'\'\'(.*?)\'\'\'/$1/;
  2499. $name = escLaTeX($name);
  2500. $desc = dewikify($wikitype, $desc);
  2501. $str .= " \\textbf{$name} & $desc \\\\ \\hline\n";
  2502. }
  2503. } else {
  2504. die("write me");
  2505. }
  2506. $str .= " \\end{tabular}\n";
  2507. $str .= "\\end{center}\n";
  2508. }
  2509. if (defined $returns) {
  2510. $returns = dewikify($wikitype, $returns);
  2511. $returns =~ s/\A\(.*?\)\s*//; # Chop datatype in parentheses off the front.
  2512. $str .= "\\subsection{Return Value}\n\n";
  2513. $str .= "$returns\n";
  2514. }
  2515. if (defined $remarks) {
  2516. $str .= "\\subsection{Remarks}\n\n";
  2517. $str .= $remarks . "\n";
  2518. }
  2519. if (defined $examples) {
  2520. $str .= "\\subsection{Code Examples}\n\n";
  2521. $dewikify_manpage_code_indent = 0;
  2522. $str .= dewikify($wikitype, $examples) . "\n";
  2523. $dewikify_manpage_code_indent = 1;
  2524. }
  2525. if (defined $threadsafety) {
  2526. $str .= "\\subsection{Thread Safety}\n\n";
  2527. $str .= dewikify($wikitype, $threadsafety) . "\n";
  2528. }
  2529. if (defined $version) {
  2530. $str .= "\\subsection{Version}\n\n";
  2531. $str .= dewikify($wikitype, $version) . "\n";
  2532. }
  2533. if (defined $related) {
  2534. $str .= "\\subsection{See Also}\n\n";
  2535. $str .= "\\begin{itemize}\n";
  2536. # !!! FIXME: lots of code duplication in all of these.
  2537. my $v = dewikify($wikitype, $related);
  2538. my @desclines = split /\n/, $v;
  2539. my $nextstr = '';
  2540. foreach (@desclines) {
  2541. s/\(\)\Z//; # Convert "SDL_Func()" to "SDL_Func"
  2542. s/\[\[(.*?)\]\]/$1/; # in case some wikilinks remain.
  2543. s/\[(.*?)\]\(.*?\)/$1/; # in case some wikilinks remain.
  2544. s/\A\*\s*\Z//;
  2545. s/\A\s*\\item\s*//;
  2546. s/\A\/*//;
  2547. s/\A\s*[\:\*\-]\s*//;
  2548. s/\A\s+//;
  2549. s/\s+\Z//;
  2550. next if $_ eq '';
  2551. next if $_ eq '\begin{itemize}';
  2552. next if $_ eq '\end{itemize}';
  2553. $str .= " \\item $_\n";
  2554. }
  2555. $str .= "\\end{itemize}\n";
  2556. $str .= "\n";
  2557. }
  2558. # !!! FIXME: Maybe put copyright in the book intro?
  2559. if (0) {
  2560. $str .= ".SH COPYRIGHT\n";
  2561. $str .= "This manpage is licensed under\n";
  2562. $str .= ".UR https://creativecommons.org/licenses/by/4.0/\n";
  2563. $str .= "Creative Commons Attribution 4.0 International (CC BY 4.0)\n";
  2564. $str .= ".UE\n";
  2565. $str .= ".PP\n";
  2566. $str .= "This manpage was generated from\n";
  2567. $str .= ".UR $wikiurl/$sym\n";
  2568. $str .= "${projectshortname}'s wiki\n";
  2569. $str .= ".UE\n";
  2570. $str .= "using SDL/build-scripts/wikiheaders.pl";
  2571. $str .= " revision $gitrev" if $gitrev ne '';
  2572. $str .= ".\n";
  2573. $str .= "Please report issues in this manpage at\n";
  2574. $str .= ".UR $bugreporturl\n";
  2575. $str .= "our bugtracker!\n";
  2576. $str .= ".UE\n";
  2577. }
  2578. $str .= "\\clearpage\n\n";
  2579. print TEXFH $str;
  2580. }
  2581. print TEXFH "\\end{document}\n\n";
  2582. close(TEXFH);
  2583. rename($latex_tmpfname, $latex_fname) or die("Can't rename '$latex_tmpfname' to '$latex_fname': $!\n");
  2584. } elsif ($copy_direction == -3) { # --report-coverage-gaps
  2585. foreach (@coverage_gap) {
  2586. print("$_\n");
  2587. }
  2588. }
  2589. # end of wikiheaders.pl ...