c2man.pl 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380
  1. #! /usr/bin/perl -w
  2. #
  3. # Generate API documentation. See https://www.winehq.org/docs/winedev-guide/api-docs for details.
  4. #
  5. # Copyright (C) 2000 Mike McCormack
  6. # Copyright (C) 2003 Jon Griffiths
  7. #
  8. # This library is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU Lesser General Public
  10. # License as published by the Free Software Foundation; either
  11. # version 2.1 of the License, or (at your option) any later version.
  12. #
  13. # This library is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. # Lesser General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public
  19. # License along with this library; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  21. #
  22. # TODO
  23. # Consolidate A+W pairs together, and only write one doc, without the suffix
  24. # Implement automatic docs of structs/defines in headers
  25. # SGML gurus - feel free to smarten up the SGML.
  26. # Add any other relevant information for the dll - imports etc
  27. # Should we have a special output mode for WineHQ?
  28. use strict;
  29. use bytes;
  30. # Function flags. most of these come from the spec flags
  31. my $FLAG_DOCUMENTED = 1;
  32. my $FLAG_NONAME = 2;
  33. my $FLAG_I386 = 4;
  34. my $FLAG_REGISTER = 8;
  35. my $FLAG_APAIR = 16; # The A version of a matching W function
  36. my $FLAG_WPAIR = 32; # The W version of a matching A function
  37. my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function
  38. # Export list slot labels.
  39. my $EXPORT_ORDINAL = 0; # Ordinal.
  40. my $EXPORT_CALL = 1; # Call type.
  41. my $EXPORT_EXPNAME = 2; # Export name.
  42. my $EXPORT_IMPNAME = 3; # Implementation name.
  43. my $EXPORT_FLAGS = 4; # Flags - see above.
  44. # Options
  45. my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
  46. my $opt_manual_section = "3w";
  47. my $opt_source_dir = ".";
  48. my $opt_parent_dir = "";
  49. my $opt_wine_root_dir = "";
  50. my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml, 'x' = xml
  51. my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
  52. my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
  53. my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
  54. my @opt_header_file_list = ();
  55. my @opt_spec_file_list = ();
  56. my @opt_source_file_list = ();
  57. # All the collected details about all the .spec files being processed
  58. my %spec_files;
  59. # All the collected details about all the source files being processed
  60. my %source_files;
  61. # All documented functions that are to be placed in the index
  62. my @index_entries_list = ();
  63. # useful globals
  64. my $pwd = `pwd`."/";
  65. $pwd =~ s/\n//;
  66. my @datetime = localtime;
  67. my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  68. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
  69. my $year = $datetime[5] + 1900;
  70. my $date = "$months[$datetime[4]] $year";
  71. sub output_api_comment($);
  72. sub output_api_footer($);
  73. sub output_api_header($);
  74. sub output_api_name($);
  75. sub output_api_synopsis($);
  76. sub output_close_api_file();
  77. sub output_comment($);
  78. sub output_html_index_files();
  79. sub output_html_stylesheet();
  80. sub output_open_api_file($);
  81. sub output_sgml_dll_file($);
  82. sub output_xml_dll_file($);
  83. sub output_sgml_master_file($);
  84. sub output_xml_master_file($);
  85. sub output_spec($);
  86. sub process_comment($);
  87. sub process_extra_comment($);
  88. # Generate the list of exported entries for the dll
  89. sub process_spec_file($)
  90. {
  91. my $spec_name = shift;
  92. (my $basename = $spec_name) =~ s/.*\///;
  93. my ($dll_name, $dll_ext) = split(/\./, $basename);
  94. $dll_ext = "dll" if ( $dll_ext eq "spec" );
  95. my $uc_dll_name = uc $dll_name;
  96. my $spec_details =
  97. {
  98. NAME => $basename,
  99. DLL_NAME => $dll_name,
  100. DLL_EXT => $dll_ext,
  101. NUM_EXPORTS => 0,
  102. NUM_STUBS => 0,
  103. NUM_FUNCS => 0,
  104. NUM_FORWARDS => 0,
  105. NUM_VARS => 0,
  106. NUM_DOCS => 0,
  107. CONTRIBUTORS => [ ],
  108. SOURCES => [ ],
  109. DESCRIPTION => [ ],
  110. EXPORTS => [ ],
  111. EXPORTED_NAMES => { },
  112. IMPLEMENTATION_NAMES => { },
  113. EXTRA_COMMENTS => [ ],
  114. CURRENT_EXTRA => [ ] ,
  115. };
  116. if ($opt_verbose > 0)
  117. {
  118. print "Processing ".$spec_name."\n";
  119. }
  120. # We allow opening to fail just to cater for the peculiarities of
  121. # the Wine build system. This doesn't hurt, in any case
  122. open(SPEC_FILE, "<$spec_name")
  123. || (($opt_source_dir ne "")
  124. && open(SPEC_FILE, "<$opt_source_dir/$spec_name"))
  125. || return;
  126. while(<SPEC_FILE>)
  127. {
  128. s/^\s+//; # Strip leading space
  129. s/\s+\n$/\n/; # Strip trailing space
  130. s/\s+/ /g; # Strip multiple tabs & spaces to a single space
  131. s/\s*#.*//; # Strip comments
  132. s/\(.*\)/ /; # Strip arguments
  133. s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
  134. s/\n$//; # Strip newline
  135. my $flags = 0;
  136. if( /\-noname/ )
  137. {
  138. $flags |= $FLAG_NONAME;
  139. }
  140. if( /\-i386/ )
  141. {
  142. $flags |= $FLAG_I386;
  143. }
  144. if( /\-register/ )
  145. {
  146. $flags |= $FLAG_REGISTER;
  147. }
  148. s/ \-[a-z0-9=_]+//g; # Strip flags
  149. if( /^(([0-9]+)|@) / )
  150. {
  151. # This line contains an exported symbol
  152. my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
  153. for ($call_convention)
  154. {
  155. /^(cdecl|stdcall|varargs|pascal)$/
  156. && do { $spec_details->{NUM_FUNCS}++; last; };
  157. /^(variable|equate)$/
  158. && do { $spec_details->{NUM_VARS}++; last; };
  159. /^(extern)$/
  160. && do { $spec_details->{NUM_FORWARDS}++; last; };
  161. /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
  162. if ($opt_verbose > 0)
  163. {
  164. print "Warning: didn't recognise convention \'",$call_convention,"'\n";
  165. }
  166. last;
  167. }
  168. # Convert ordinal only names so we can find them later
  169. if ($exported_name eq "@")
  170. {
  171. $exported_name = $uc_dll_name.'_'.$ordinal;
  172. }
  173. if (!defined($implementation_name))
  174. {
  175. $implementation_name = $exported_name;
  176. }
  177. if ($implementation_name eq "")
  178. {
  179. $implementation_name = $exported_name;
  180. }
  181. if ($implementation_name =~ /(.*?)\./)
  182. {
  183. $call_convention = "forward"; # Referencing a function from another dll
  184. $spec_details->{NUM_FUNCS}--;
  185. $spec_details->{NUM_FORWARDS}++;
  186. }
  187. # Add indices for the exported and implementation names
  188. $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
  189. if ($implementation_name ne $exported_name)
  190. {
  191. $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
  192. }
  193. # Add the exported entry
  194. $spec_details->{NUM_EXPORTS}++;
  195. my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
  196. push (@{$spec_details->{EXPORTS}},[@export]);
  197. }
  198. }
  199. close(SPEC_FILE);
  200. # Add this .spec files details to the list of .spec files
  201. $spec_files{$uc_dll_name} = [$spec_details];
  202. }
  203. # Read each source file, extract comments, and generate API documentation if appropriate
  204. sub process_source_file($)
  205. {
  206. my $source_file = shift;
  207. my $source_details =
  208. {
  209. CONTRIBUTORS => [ ],
  210. DEBUG_CHANNEL => "",
  211. };
  212. my $comment =
  213. {
  214. FILE => $source_file,
  215. COMMENT_NAME => "",
  216. ALT_NAME => "",
  217. DLL_NAME => "",
  218. DLL_EXT => "",
  219. ORDINAL => "",
  220. RETURNS => "",
  221. PROTOTYPE => [],
  222. TEXT => [],
  223. };
  224. my $parse_state = 0;
  225. my $ignore_blank_lines = 1;
  226. my $extra_comment = 0; # 1 if this is an extra comment, i.e it's not a .spec export
  227. if ($opt_verbose > 0)
  228. {
  229. print "Processing ".$source_file."\n";
  230. }
  231. open(SOURCE_FILE,"<$source_file")
  232. || (($opt_source_dir ne ".")
  233. && open(SOURCE_FILE,"<$opt_source_dir/$source_file"))
  234. || (($opt_parent_dir ne "")
  235. && open(SOURCE_FILE,"<$opt_source_dir/$opt_parent_dir/$source_file"))
  236. || die "couldn't open ".$source_file."\n";
  237. # Add this source file to the list of source files
  238. $source_files{$source_file} = [$source_details];
  239. while(<SOURCE_FILE>)
  240. {
  241. s/\n$//; # Strip newline
  242. s/^\s+//; # Strip leading space
  243. s/\s+$//; # Strip trailing space
  244. if (! /^\*\|/ )
  245. {
  246. # Strip multiple tabs & spaces to a single space
  247. s/\s+/ /g;
  248. }
  249. if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
  250. {
  251. # Extract a contributor to this file
  252. my $contributor = $2;
  253. $contributor =~ s/ *$//;
  254. $contributor =~ s/^by //;
  255. $contributor =~ s/\.$//;
  256. $contributor =~ s/ (for .*)/ \($1\)/;
  257. if ($contributor ne "")
  258. {
  259. if ($opt_verbose > 3)
  260. {
  261. print "Info: Found contributor:'".$contributor."'\n";
  262. }
  263. push (@{$source_details->{CONTRIBUTORS}},$contributor);
  264. }
  265. }
  266. elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
  267. {
  268. # Extract the debug channel to use
  269. if ($opt_verbose > 3)
  270. {
  271. print "Info: Found debug channel:'".$1."'\n";
  272. }
  273. $source_details->{DEBUG_CHANNEL} = $1;
  274. }
  275. if ($parse_state == 0) # Searching for a comment
  276. {
  277. if ( /^\/\**$/ )
  278. {
  279. # This file is used by the DLL - Make sure we get our contributors right
  280. @{$spec_files{$comment->{DLL_NAME}}[0]->{CURRENT_EXTRA}} = ();
  281. push (@{$spec_files{$comment->{DLL_NAME}}[0]->{SOURCES}},$comment->{FILE});
  282. # Found a comment start
  283. $comment->{COMMENT_NAME} = "";
  284. $comment->{ALT_NAME} = "";
  285. $comment->{DLL_NAME} = "";
  286. $comment->{ORDINAL} = "";
  287. $comment->{RETURNS} = "";
  288. $comment->{PROTOTYPE} = [];
  289. $comment->{TEXT} = [];
  290. $ignore_blank_lines = 1;
  291. $extra_comment = 0;
  292. $parse_state = 3;
  293. }
  294. }
  295. elsif ($parse_state == 1) # Reading in a comment
  296. {
  297. if ( /^\**\// )
  298. {
  299. # Found the end of the comment
  300. $parse_state = 2;
  301. }
  302. elsif ( s/^\*\|/\|/ )
  303. {
  304. # A line of comment not meant to be pre-processed
  305. push (@{$comment->{TEXT}},$_); # Add the comment text
  306. }
  307. elsif ( s/^ *\** *// )
  308. {
  309. # A line of comment, starting with an asterisk
  310. if ( /^[A-Z]+$/ || $_ eq "")
  311. {
  312. # This is a section start, so skip blank lines before and after it.
  313. my $last_line = pop(@{$comment->{TEXT}});
  314. if (defined($last_line) && $last_line ne "")
  315. {
  316. # Put it back
  317. push (@{$comment->{TEXT}},$last_line);
  318. }
  319. if ( /^[A-Z]+$/ )
  320. {
  321. $ignore_blank_lines = 1;
  322. }
  323. else
  324. {
  325. $ignore_blank_lines = 0;
  326. }
  327. }
  328. if ($ignore_blank_lines == 0 || $_ ne "")
  329. {
  330. push (@{$comment->{TEXT}},$_); # Add the comment text
  331. }
  332. }
  333. else
  334. {
  335. # This isn't a well formatted comment: look for the next one
  336. $parse_state = 0;
  337. }
  338. }
  339. elsif ($parse_state == 2) # Finished reading in a comment
  340. {
  341. if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
  342. /.*?\(/ )
  343. {
  344. # Comment is followed by a function definition
  345. $parse_state = 4; # Fall through to read prototype
  346. }
  347. else
  348. {
  349. # Allow cpp directives and blank lines between the comment and prototype
  350. if ($extra_comment == 1)
  351. {
  352. # An extra comment not followed by a function definition
  353. $parse_state = 5; # Fall through to process comment
  354. }
  355. elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
  356. {
  357. # This isn't a well formatted comment: look for the next one
  358. if ($opt_verbose > 1)
  359. {
  360. print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
  361. }
  362. $parse_state = 0;
  363. }
  364. }
  365. }
  366. elsif ($parse_state == 3) # Reading in the first line of a comment
  367. {
  368. s/^ *\** *//;
  369. if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ )
  370. {
  371. # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
  372. if (defined ($7) && $7 ne "")
  373. {
  374. push (@{$comment->{TEXT}},$_); # Add the trailing comment text
  375. }
  376. $comment->{COMMENT_NAME} = $1;
  377. $comment->{DLL_NAME} = uc $3;
  378. $comment->{ORDINAL} = $4;
  379. $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
  380. $parse_state = 1;
  381. }
  382. elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ )
  383. {
  384. # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
  385. $comment->{COMMENT_NAME} = $1;
  386. $comment->{DLL_NAME} = uc $2;
  387. $comment->{ORDINAL} = "";
  388. $extra_comment = 1;
  389. $parse_state = 1;
  390. }
  391. else
  392. {
  393. # This isn't a well formatted comment: look for the next one
  394. $parse_state = 0;
  395. }
  396. }
  397. if ($parse_state == 4) # Reading in the function definition
  398. {
  399. push (@{$comment->{PROTOTYPE}},$_);
  400. # Strip comments from the line before checking for ')'
  401. my $stripped_line = $_;
  402. $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
  403. if ( $stripped_line =~ /\)/ )
  404. {
  405. # Strip a blank last line
  406. my $last_line = pop(@{$comment->{TEXT}});
  407. if (defined($last_line) && $last_line ne "")
  408. {
  409. # Put it back
  410. push (@{$comment->{TEXT}},$last_line);
  411. }
  412. if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
  413. {
  414. # Create a 'not implemented' comment
  415. @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
  416. }
  417. $parse_state = 5;
  418. }
  419. }
  420. if ($parse_state == 5) # Processing the comment
  421. {
  422. # Process it, if it has any text
  423. if (@{$comment->{TEXT}} > 0)
  424. {
  425. if ($extra_comment == 1)
  426. {
  427. process_extra_comment($comment);
  428. }
  429. else
  430. {
  431. @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
  432. process_comment($comment);
  433. }
  434. }
  435. elsif ($opt_verbose > 1 && $opt_output_empty == 0)
  436. {
  437. print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
  438. }
  439. $parse_state = 0;
  440. }
  441. }
  442. close(SOURCE_FILE);
  443. }
  444. # Standardise a comments text for consistency
  445. sub process_comment_text($)
  446. {
  447. my $comment = shift;
  448. my $in_params = 0;
  449. my @tmp_list = ();
  450. my $i = 0;
  451. for (@{$comment->{TEXT}})
  452. {
  453. my $line = $_;
  454. if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
  455. {
  456. $in_params = 0;
  457. }
  458. if ( $in_params > 0 && !/\[/ && !/\]/ )
  459. {
  460. # Possibly a continuation of the parameter description
  461. my $last_line = pop(@tmp_list);
  462. if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
  463. {
  464. $line = $last_line." ".$_;
  465. }
  466. else
  467. {
  468. $in_params = 0;
  469. push (@tmp_list, $last_line);
  470. }
  471. }
  472. if ( /^(PARAMS|MEMBERS)$/ )
  473. {
  474. $in_params = 1;
  475. }
  476. push (@tmp_list, $line);
  477. }
  478. @{$comment->{TEXT}} = @tmp_list;
  479. for (@{$comment->{TEXT}})
  480. {
  481. if (! /^\|/ )
  482. {
  483. # Map I/O values. These come in too many formats to standardise now....
  484. s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
  485. s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
  486. s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
  487. # TRUE/FALSE/NULL are defines, capitalise them
  488. s/True|true/TRUE/g;
  489. s/False|false/FALSE/g;
  490. s/Null|null/NULL/g;
  491. # Preferred capitalisations
  492. s/ wine| WINE/ Wine/g;
  493. s/ API | api / Api /g;
  494. s/ DLL | Dll / dll /g;
  495. s/ URL | url / Url /g;
  496. s/WIN16|win16/Win16/g;
  497. s/WIN32|win32/Win32/g;
  498. s/WIN64|win64/Win64/g;
  499. s/ ID | id / Id /g;
  500. # Grammar
  501. s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
  502. s/ \:/\:/g; # Colons to the left
  503. s/ \;/\;/g; # Semi-colons too
  504. # Common idioms
  505. s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
  506. s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
  507. s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
  508. s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
  509. # Trademarks
  510. s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
  511. s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
  512. s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
  513. s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
  514. s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
  515. # Abbreviations
  516. s/( char )/ character /g;
  517. s/( chars )/ characters /g;
  518. s/( info )/ information /g;
  519. s/( app )/ application /g;
  520. s/( apps )/ applications /g;
  521. s/( exe )/ executable /g;
  522. s/( ptr )/ pointer /g;
  523. s/( obj )/ object /g;
  524. s/( err )/ error /g;
  525. s/( bool )/ boolean /g;
  526. s/( no\. )/ number /g;
  527. s/( No\. )/ Number /g;
  528. # Punctuation
  529. if ( /\[I|\[O/ && ! /\.$/ )
  530. {
  531. $_ = $_."."; # Always have a full stop at the end of parameter desc.
  532. }
  533. elsif ($i > 0 && /^[A-Z]*$/ &&
  534. !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
  535. !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
  536. {
  537. if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
  538. {
  539. # Paragraphs always end with a full stop
  540. @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
  541. }
  542. }
  543. }
  544. $i++;
  545. }
  546. }
  547. # Standardise our comment and output it if it is suitable.
  548. sub process_comment($)
  549. {
  550. my $comment = shift;
  551. # Don't process this comment if the function isn't exported
  552. my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
  553. if (!defined($spec_details))
  554. {
  555. if ($opt_verbose > 2)
  556. {
  557. print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
  558. $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
  559. }
  560. return;
  561. }
  562. if ($comment->{COMMENT_NAME} eq "@")
  563. {
  564. my $found = 0;
  565. # Find the name from the .spec file
  566. for (@{$spec_details->{EXPORTS}})
  567. {
  568. if (@$_[$EXPORT_ORDINAL] eq $comment->{ORDINAL})
  569. {
  570. $comment->{COMMENT_NAME} = @$_[$EXPORT_EXPNAME];
  571. $found = 1;
  572. }
  573. }
  574. if ($found == 0)
  575. {
  576. # Create an implementation name
  577. $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
  578. }
  579. }
  580. my $exported_names = $spec_details->{EXPORTED_NAMES};
  581. my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
  582. my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
  583. if (!defined($export_index))
  584. {
  585. # Perhaps the comment uses the implementation name?
  586. $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
  587. }
  588. if (!defined($export_index))
  589. {
  590. # This function doesn't appear to be exported. hmm.
  591. if ($opt_verbose > 2)
  592. {
  593. print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
  594. $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
  595. }
  596. return;
  597. }
  598. # When the function is exported twice we have the second name below the first
  599. # (you see this a lot in ntdll, but also in some other places).
  600. my $first_line = ${$comment->{TEXT}}[1];
  601. if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
  602. {
  603. # Found a second name - mark it as documented
  604. my $alt_index = $exported_names->{$1};
  605. if (defined($alt_index))
  606. {
  607. if ($opt_verbose > 2)
  608. {
  609. print "Info: Found alternate name '",$1,"\n";
  610. }
  611. my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
  612. @$alt_export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
  613. $spec_details->{NUM_DOCS}++;
  614. ${$comment->{TEXT}}[1] = "";
  615. }
  616. }
  617. if (@{$spec_details->{CURRENT_EXTRA}})
  618. {
  619. # We have an extra comment that might be related to this one
  620. my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
  621. my $current_name = $current_comment->{COMMENT_NAME};
  622. if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
  623. {
  624. if ($opt_verbose > 2)
  625. {
  626. print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
  627. }
  628. # Add a reference to this comment to our extra comment
  629. push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
  630. }
  631. }
  632. # We want our docs generated using the implementation name, so they are unique
  633. my $export = @{$spec_details->{EXPORTS}}[$export_index];
  634. $comment->{COMMENT_NAME} = @$export[$EXPORT_IMPNAME];
  635. $comment->{ALT_NAME} = @$export[$EXPORT_EXPNAME];
  636. # Mark the function as documented
  637. $spec_details->{NUM_DOCS}++;
  638. @$export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
  639. # If we have parameter comments in the prototype, extract them
  640. my @parameter_comments;
  641. for (@{$comment->{PROTOTYPE}})
  642. {
  643. s/ *\, */\,/g; # Strip spaces from around commas
  644. if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
  645. {
  646. my $parameter_comment = $2;
  647. if (!$parameter_comment =~ /^\[/ )
  648. {
  649. # Add [IO] markers so we format the comment correctly
  650. $parameter_comment = "[fixme] ".$parameter_comment;
  651. }
  652. if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
  653. {
  654. # Add the parameter name
  655. $parameter_comment = $2." ".$parameter_comment;
  656. }
  657. push (@parameter_comments, $parameter_comment);
  658. }
  659. }
  660. # If we extracted any prototype comments, add them to the comment text.
  661. if (@parameter_comments)
  662. {
  663. @parameter_comments = ("PARAMS", @parameter_comments);
  664. my @new_comment = ();
  665. my $inserted_params = 0;
  666. for (@{$comment->{TEXT}})
  667. {
  668. if ( $inserted_params == 0 && /^[A-Z]+$/ )
  669. {
  670. # Found a section header, so this is where we insert
  671. push (@new_comment, @parameter_comments);
  672. $inserted_params = 1;
  673. }
  674. push (@new_comment, $_);
  675. }
  676. if ($inserted_params == 0)
  677. {
  678. # Add them to the end
  679. push (@new_comment, @parameter_comments);
  680. }
  681. $comment->{TEXT} = [@new_comment];
  682. }
  683. if ($opt_fussy == 1 && $opt_output_empty == 0)
  684. {
  685. # Reject any comment that doesn't have a description or a RETURNS section.
  686. # This is the default for now, 'coz many comments aren't suitable.
  687. my $found_returns = 0;
  688. my $found_description_text = 0;
  689. my $in_description = 0;
  690. for (@{$comment->{TEXT}})
  691. {
  692. if ( /^RETURNS$/ )
  693. {
  694. $found_returns = 1;
  695. $in_description = 0;
  696. }
  697. elsif ( /^DESCRIPTION$/ )
  698. {
  699. $in_description = 1;
  700. }
  701. elsif ($in_description == 1)
  702. {
  703. if ( !/^[A-Z]+$/ )
  704. {
  705. # Don't reject comments that refer to another doc (e.g. A/W)
  706. if ( /^See ([A-Za-z0-9_]+)\.$/ )
  707. {
  708. if ($comment->{COMMENT_NAME} =~ /W$/ )
  709. {
  710. # This is probably a Unicode version of an Ascii function.
  711. # Create the Ascii name and see if it has been documented
  712. my $ascii_name = $comment->{COMMENT_NAME};
  713. $ascii_name =~ s/W$/A/;
  714. my $ascii_export_index = $exported_names->{$ascii_name};
  715. if (!defined($ascii_export_index))
  716. {
  717. $ascii_export_index = $implementation_names->{$ascii_name};
  718. }
  719. if (!defined($ascii_export_index))
  720. {
  721. if ($opt_verbose > 2)
  722. {
  723. print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
  724. }
  725. }
  726. else
  727. {
  728. my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
  729. if (@$ascii_export[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
  730. {
  731. # Flag these functions as an A/W pair
  732. @$ascii_export[$EXPORT_FLAGS] |= $FLAG_APAIR;
  733. @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR;
  734. }
  735. }
  736. }
  737. $found_returns = 1;
  738. }
  739. elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
  740. {
  741. @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR; # Explicitly marked as W version
  742. $found_returns = 1;
  743. }
  744. elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
  745. {
  746. @$export[$EXPORT_FLAGS] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
  747. $found_returns = 1;
  748. }
  749. $found_description_text = 1;
  750. }
  751. else
  752. {
  753. $in_description = 0;
  754. }
  755. }
  756. }
  757. if ($found_returns == 0 || $found_description_text == 0)
  758. {
  759. if ($opt_verbose > 2)
  760. {
  761. print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
  762. "description and/or RETURNS section, skipping\n";
  763. }
  764. $spec_details->{NUM_DOCS}--;
  765. @$export[$EXPORT_FLAGS] &= ~$FLAG_DOCUMENTED;
  766. return;
  767. }
  768. }
  769. process_comment_text($comment);
  770. # Strip the prototypes return value, call convention, name and brackets
  771. # (This leaves it as a list of types and names, or empty for void functions)
  772. my $prototype = join(" ", @{$comment->{PROTOTYPE}});
  773. $prototype =~ s/ / /g;
  774. if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
  775. {
  776. $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
  777. $comment->{RETURNS} = $1;
  778. }
  779. else
  780. {
  781. $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
  782. $comment->{RETURNS} = $1;
  783. }
  784. $prototype =~ s/ *\).*//; # Strip end bracket
  785. $prototype =~ s/ *\* */\*/g; # Strip space around pointers
  786. $prototype =~ s/ *\, */\,/g; # Strip space around commas
  787. $prototype =~ s/^(void|VOID)$//; # If void, leave blank
  788. $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
  789. @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
  790. # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
  791. # Find header file
  792. my $h_file = "";
  793. if (@$export[$EXPORT_FLAGS] & $FLAG_NONAME)
  794. {
  795. $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
  796. }
  797. else
  798. {
  799. if ($comment->{COMMENT_NAME} ne "")
  800. {
  801. my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
  802. $tmp = `$tmp`;
  803. my $exit_value = $? >> 8;
  804. if ($exit_value == 0)
  805. {
  806. $tmp =~ s/\n.*//g;
  807. if ($tmp ne "")
  808. {
  809. $h_file = "$tmp";
  810. $h_file =~ s|^.*/\./||;
  811. }
  812. }
  813. }
  814. elsif ($comment->{ALT_NAME} ne "")
  815. {
  816. my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
  817. $tmp = `$tmp`;
  818. my $exit_value = $? >> 8;
  819. if ($exit_value == 0)
  820. {
  821. $tmp =~ s/\n.*//g;
  822. if ($tmp ne "")
  823. {
  824. $h_file = "$tmp";
  825. $h_file =~ s|^.*/\./||;
  826. }
  827. }
  828. }
  829. $h_file =~ s/^ *//;
  830. $h_file =~ s/\n//;
  831. if ($h_file eq "")
  832. {
  833. $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
  834. }
  835. else
  836. {
  837. $h_file = "Declared in \"".$h_file."\".";
  838. }
  839. }
  840. # Find source file
  841. my $c_file = $comment->{FILE};
  842. if ($opt_wine_root_dir ne "")
  843. {
  844. my $cfile = $pwd."/".$c_file; # Current dir + file
  845. $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
  846. $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
  847. $cfile =~ s/\n//; # Strip newline
  848. my $newfile = $c_file;
  849. $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
  850. $cfile = $cfile."/".$newfile; # Append filename to base path
  851. $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
  852. $cfile =~ s/\/\//\//g; # Remove any double slashes
  853. $cfile =~ s/^\/+//; # Strip initial directory slash
  854. $c_file = $cfile;
  855. }
  856. $c_file = "Implemented in \"".$c_file."\".";
  857. # Add the implementation details
  858. push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
  859. if (@$export[$EXPORT_FLAGS] & $FLAG_I386)
  860. {
  861. push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
  862. }
  863. if (@$export[$EXPORT_FLAGS] & $FLAG_REGISTER)
  864. {
  865. push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
  866. "For more details, please read the source code.");
  867. }
  868. my $source_details = $source_files{$comment->{FILE}}[0];
  869. if ($source_details->{DEBUG_CHANNEL} ne "")
  870. {
  871. push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
  872. }
  873. # Write out the documentation for the API
  874. output_comment($comment)
  875. }
  876. # process our extra comment and output it if it is suitable.
  877. sub process_extra_comment($)
  878. {
  879. my $comment = shift;
  880. my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
  881. if (!defined($spec_details))
  882. {
  883. if ($opt_verbose > 2)
  884. {
  885. print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
  886. $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
  887. }
  888. return;
  889. }
  890. # Check first to see if this is documentation for the DLL.
  891. if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
  892. {
  893. if ($opt_verbose > 2)
  894. {
  895. print "Info: Found DLL documentation\n";
  896. }
  897. for (@{$comment->{TEXT}})
  898. {
  899. push (@{$spec_details->{DESCRIPTION}}, $_);
  900. }
  901. return;
  902. }
  903. # Add the comment to the DLL page as a link
  904. push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
  905. # If we have a prototype, process as a regular comment
  906. if (@{$comment->{PROTOTYPE}})
  907. {
  908. $comment->{ORDINAL} = "@";
  909. # Add an index for the comment name
  910. $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
  911. # Add a fake exported entry
  912. $spec_details->{NUM_EXPORTS}++;
  913. my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
  914. ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
  915. my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
  916. push (@{$spec_details->{EXPORTS}},[@export]);
  917. @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
  918. process_comment($comment);
  919. return;
  920. }
  921. if ($opt_verbose > 0)
  922. {
  923. print "Processing ",$comment->{COMMENT_NAME},"\n";
  924. }
  925. if (@{$spec_details->{CURRENT_EXTRA}})
  926. {
  927. my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
  928. if ($opt_verbose > 0)
  929. {
  930. print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
  931. }
  932. # Output the current comment
  933. process_comment_text($current_comment);
  934. output_open_api_file($current_comment->{COMMENT_NAME});
  935. output_api_header($current_comment);
  936. output_api_name($current_comment);
  937. output_api_comment($current_comment);
  938. output_api_footer($current_comment);
  939. output_close_api_file();
  940. }
  941. if ($opt_verbose > 2)
  942. {
  943. print "Setting current to ",$comment->{COMMENT_NAME},"\n";
  944. }
  945. my $comment_copy =
  946. {
  947. FILE => $comment->{FILE},
  948. COMMENT_NAME => $comment->{COMMENT_NAME},
  949. ALT_NAME => $comment->{ALT_NAME},
  950. DLL_NAME => $comment->{DLL_NAME},
  951. ORDINAL => $comment->{ORDINAL},
  952. RETURNS => $comment->{RETURNS},
  953. PROTOTYPE => [],
  954. TEXT => [],
  955. };
  956. for (@{$comment->{TEXT}})
  957. {
  958. push (@{$comment_copy->{TEXT}}, $_);
  959. }
  960. # Set this comment to be the current extra comment
  961. @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
  962. }
  963. # Write a standardised comment out in the appropriate format
  964. sub output_comment($)
  965. {
  966. my $comment = shift;
  967. if ($opt_verbose > 0)
  968. {
  969. print "Processing ",$comment->{COMMENT_NAME},"\n";
  970. }
  971. if ($opt_verbose > 4)
  972. {
  973. print "--PROTO--\n";
  974. for (@{$comment->{PROTOTYPE}})
  975. {
  976. print "'".$_."'\n";
  977. }
  978. print "--COMMENT--\n";
  979. for (@{$comment->{TEXT} })
  980. {
  981. print $_."\n";
  982. }
  983. }
  984. output_open_api_file($comment->{COMMENT_NAME});
  985. output_api_header($comment);
  986. output_api_name($comment);
  987. output_api_synopsis($comment);
  988. output_api_comment($comment);
  989. output_api_footer($comment);
  990. output_close_api_file();
  991. }
  992. # Write out an index file for each .spec processed
  993. sub process_index_files()
  994. {
  995. foreach my $spec_file (keys %spec_files)
  996. {
  997. my $spec_details = $spec_files{$spec_file}[0];
  998. if (defined ($spec_details->{DLL_NAME}))
  999. {
  1000. if (@{$spec_details->{CURRENT_EXTRA}})
  1001. {
  1002. # We have an unwritten extra comment, write it
  1003. my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
  1004. process_extra_comment($current_comment);
  1005. @{$spec_details->{CURRENT_EXTRA}} = ();
  1006. }
  1007. output_spec($spec_details);
  1008. }
  1009. }
  1010. }
  1011. # Write a spec files documentation out in the appropriate format
  1012. sub output_spec($)
  1013. {
  1014. my $spec_details = shift;
  1015. if ($opt_verbose > 2)
  1016. {
  1017. print "Writing:",$spec_details->{DLL_NAME},"\n";
  1018. }
  1019. # Use the comment output functions for consistency
  1020. my $comment =
  1021. {
  1022. FILE => $spec_details->{DLL_NAME},
  1023. COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
  1024. ALT_NAME => $spec_details->{DLL_NAME},
  1025. DLL_NAME => "",
  1026. ORDINAL => "",
  1027. RETURNS => "",
  1028. PROTOTYPE => [],
  1029. TEXT => [],
  1030. };
  1031. my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
  1032. $spec_details->{NUM_FUNCS};
  1033. my $percent_implemented = 0;
  1034. if ($total_implemented)
  1035. {
  1036. $percent_implemented = $total_implemented /
  1037. ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
  1038. }
  1039. $percent_implemented = int($percent_implemented);
  1040. my $percent_documented = 0;
  1041. if ($spec_details->{NUM_DOCS})
  1042. {
  1043. # Treat forwards and data as documented funcs for statistics
  1044. $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
  1045. $percent_documented = int($percent_documented);
  1046. }
  1047. # Make a list of the contributors to this DLL.
  1048. my @contributors;
  1049. foreach my $source_file (keys %source_files)
  1050. {
  1051. my $source_details = $source_files{$source_file}[0];
  1052. for (@{$source_details->{CONTRIBUTORS}})
  1053. {
  1054. push (@contributors, $_);
  1055. }
  1056. }
  1057. my %saw;
  1058. @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
  1059. @contributors = sort @contributors;
  1060. # Remove duplicates and blanks
  1061. for(my $i=0; $i<@contributors; $i++)
  1062. {
  1063. if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
  1064. {
  1065. $contributors[$i-1] = $contributors[$i];
  1066. }
  1067. }
  1068. undef %saw;
  1069. @contributors = grep(!$saw{$_}++, @contributors);
  1070. if ($opt_verbose > 3)
  1071. {
  1072. print "Contributors:\n";
  1073. for (@contributors)
  1074. {
  1075. print "'".$_."'\n";
  1076. }
  1077. }
  1078. my $contribstring = join (", ", @contributors);
  1079. # Create the initial comment text
  1080. @{$comment->{TEXT}} = (
  1081. "NAME",
  1082. $comment->{COMMENT_NAME}
  1083. );
  1084. # Add the description, if we have one
  1085. if (@{$spec_details->{DESCRIPTION}})
  1086. {
  1087. push (@{$comment->{TEXT}}, "DESCRIPTION");
  1088. for (@{$spec_details->{DESCRIPTION}})
  1089. {
  1090. push (@{$comment->{TEXT}}, $_);
  1091. }
  1092. }
  1093. # Add the statistics and contributors
  1094. push (@{$comment->{TEXT}},
  1095. "STATISTICS",
  1096. "Forwards: ".$spec_details->{NUM_FORWARDS},
  1097. "Variables: ".$spec_details->{NUM_VARS},
  1098. "Stubs: ".$spec_details->{NUM_STUBS},
  1099. "Functions: ".$spec_details->{NUM_FUNCS},
  1100. "Exports-Total: ".$spec_details->{NUM_EXPORTS},
  1101. "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
  1102. "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
  1103. "CONTRIBUTORS",
  1104. "The following people hold copyrights on the source files comprising this dll:",
  1105. "",
  1106. $contribstring,
  1107. "Note: This list may not be complete.",
  1108. "For a complete listing, see the git commit logs and the File \"AUTHORS\" in the Wine source tree.",
  1109. "",
  1110. );
  1111. if ($opt_output_format eq "h")
  1112. {
  1113. # Add the exports to the comment text
  1114. push (@{$comment->{TEXT}},"EXPORTS");
  1115. my $exports = $spec_details->{EXPORTS};
  1116. for (@$exports)
  1117. {
  1118. my $line = "";
  1119. # @$_ => ordinal, call convention, exported name, implementation name, flags;
  1120. if (@$_[$EXPORT_CALL] eq "forward")
  1121. {
  1122. my $forward_dll = @$_[$EXPORT_IMPNAME];
  1123. $forward_dll =~ s/\.(.*)//;
  1124. $line = @$_[$EXPORT_EXPNAME]." (forward to ".$1."() in ".$forward_dll."())";
  1125. }
  1126. elsif (@$_[$EXPORT_CALL] eq "extern")
  1127. {
  1128. $line = @$_[$EXPORT_EXPNAME]." (extern)";
  1129. }
  1130. elsif (@$_[$EXPORT_CALL] eq "stub")
  1131. {
  1132. $line = @$_[$EXPORT_EXPNAME]." (stub)";
  1133. }
  1134. elsif (@$_[$EXPORT_CALL] eq "fake")
  1135. {
  1136. # Don't add this function here, it gets listed with the extra documentation
  1137. if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
  1138. {
  1139. # This function should be indexed
  1140. push (@index_entries_list, @$_[$EXPORT_IMPNAME].",".@$_[$EXPORT_IMPNAME]);
  1141. }
  1142. }
  1143. elsif (@$_[$EXPORT_CALL] eq "equate" || @$_[$EXPORT_CALL] eq "variable")
  1144. {
  1145. $line = @$_[$EXPORT_EXPNAME]." (data)";
  1146. }
  1147. else
  1148. {
  1149. # A function
  1150. if (@$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
  1151. {
  1152. # Documented
  1153. $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
  1154. if (@$_[$EXPORT_EXPNAME] ne @$_[$EXPORT_IMPNAME])
  1155. {
  1156. $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
  1157. }
  1158. else
  1159. {
  1160. $line = @$_[$EXPORT_EXPNAME]."()";
  1161. }
  1162. if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
  1163. {
  1164. # This function should be indexed
  1165. push (@index_entries_list, @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]);
  1166. }
  1167. }
  1168. else
  1169. {
  1170. $line = @$_[$EXPORT_EXPNAME]." (not documented)";
  1171. }
  1172. }
  1173. if ($line ne "")
  1174. {
  1175. push (@{$comment->{TEXT}}, $line, "");
  1176. }
  1177. }
  1178. # Add links to the extra documentation
  1179. if (@{$spec_details->{EXTRA_COMMENTS}})
  1180. {
  1181. push (@{$comment->{TEXT}}, "SEE ALSO");
  1182. my %htmp;
  1183. @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
  1184. for (@{$spec_details->{EXTRA_COMMENTS}})
  1185. {
  1186. push (@{$comment->{TEXT}}, $_."()", "");
  1187. }
  1188. }
  1189. }
  1190. # The dll entry should also be indexed
  1191. push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
  1192. # Write out the document
  1193. output_open_api_file($spec_details->{DLL_NAME});
  1194. output_api_header($comment);
  1195. output_api_comment($comment);
  1196. output_api_footer($comment);
  1197. output_close_api_file();
  1198. # Add this dll to the database of dll names
  1199. my $output_file = $opt_output_directory."/dlls.db";
  1200. # Append the dllname to the output db of names
  1201. open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
  1202. print DLLDB $spec_details->{DLL_NAME},"\n";
  1203. close(DLLDB);
  1204. if ($opt_output_format eq "s")
  1205. {
  1206. output_sgml_dll_file($spec_details);
  1207. return;
  1208. }
  1209. if ($opt_output_format eq "x")
  1210. {
  1211. output_xml_dll_file($spec_details);
  1212. return;
  1213. }
  1214. }
  1215. #
  1216. # OUTPUT FUNCTIONS
  1217. # ----------------
  1218. # Only these functions know anything about formatting for a specific
  1219. # output type. The functions above work only with plain text.
  1220. # This is to allow new types of output to be added easily.
  1221. # Open the api file
  1222. sub output_open_api_file($)
  1223. {
  1224. my $output_name = shift;
  1225. $output_name = $opt_output_directory."/".$output_name;
  1226. if ($opt_output_format eq "h")
  1227. {
  1228. $output_name = $output_name.".html";
  1229. }
  1230. elsif ($opt_output_format eq "s")
  1231. {
  1232. $output_name = $output_name.".sgml";
  1233. }
  1234. elsif ($opt_output_format eq "x")
  1235. {
  1236. $output_name = $output_name.".xml";
  1237. }
  1238. else
  1239. {
  1240. $output_name = $output_name.".".$opt_manual_section;
  1241. }
  1242. open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
  1243. }
  1244. # Close the api file
  1245. sub output_close_api_file()
  1246. {
  1247. close (OUTPUT);
  1248. }
  1249. # Output the api file header
  1250. sub output_api_header($)
  1251. {
  1252. my $comment = shift;
  1253. if ($opt_output_format eq "h")
  1254. {
  1255. print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
  1256. print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"https://www.w3.org/TR/html4/strict.dtd\">\n";
  1257. print OUTPUT "<HTML>\n<HEAD>\n";
  1258. print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
  1259. print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
  1260. print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
  1261. print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
  1262. }
  1263. elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
  1264. {
  1265. print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
  1266. "<sect1>\n",
  1267. "<title>$comment->{COMMENT_NAME}</title>\n";
  1268. }
  1269. else
  1270. {
  1271. print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
  1272. ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
  1273. "Wine API\" \"Wine API\"\n";
  1274. }
  1275. }
  1276. sub output_api_footer($)
  1277. {
  1278. if ($opt_output_format eq "h")
  1279. {
  1280. print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
  1281. " All trademarks are the property of their respective owners.".
  1282. " Visit <a href=\"https://www.winehq.org\">WineHQ</a> for license details.".
  1283. " Generated $date.</i></p>\n</body>\n</html>\n";
  1284. }
  1285. elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
  1286. {
  1287. print OUTPUT "</sect1>\n";
  1288. return;
  1289. }
  1290. else
  1291. {
  1292. }
  1293. }
  1294. sub output_api_section_start($$)
  1295. {
  1296. my $comment = shift;
  1297. my $section_name = shift;
  1298. if ($opt_output_format eq "h")
  1299. {
  1300. print OUTPUT "\n<h2 class=\"section\">",$section_name,"</h2>\n";
  1301. }
  1302. elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
  1303. {
  1304. print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
  1305. }
  1306. else
  1307. {
  1308. print OUTPUT "\n\.SH ",$section_name,"\n";
  1309. }
  1310. }
  1311. sub output_api_section_end()
  1312. {
  1313. # Not currently required by any output formats
  1314. }
  1315. sub output_api_name($)
  1316. {
  1317. my $comment = shift;
  1318. my $readable_name = $comment->{COMMENT_NAME};
  1319. $readable_name =~ s/-/ /g; # make section names more readable
  1320. output_api_section_start($comment,"NAME");
  1321. my $dll_ordinal = "";
  1322. if ($comment->{ORDINAL} ne "")
  1323. {
  1324. $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
  1325. }
  1326. if ($opt_output_format eq "h")
  1327. {
  1328. print OUTPUT "<p><b class=\"func_name\">",$readable_name,
  1329. "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
  1330. ,$dll_ordinal,"</i></p>\n";
  1331. }
  1332. elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
  1333. {
  1334. print OUTPUT "<para>\n <command>",$readable_name,"</command> <emphasis>",
  1335. $dll_ordinal,"</emphasis>\n</para>\n";
  1336. }
  1337. else
  1338. {
  1339. print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal;
  1340. }
  1341. output_api_section_end();
  1342. }
  1343. sub output_api_synopsis($)
  1344. {
  1345. my $comment = shift;
  1346. my @fmt;
  1347. output_api_section_start($comment,"SYNOPSIS");
  1348. if ($opt_output_format eq "h")
  1349. {
  1350. print OUTPUT "<pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
  1351. @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
  1352. }
  1353. elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
  1354. {
  1355. print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
  1356. @fmt = ("", "\n", "<emphasis>", "</emphasis>");
  1357. }
  1358. else
  1359. {
  1360. print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
  1361. @fmt = ("", "\n", "\\fI", "\\fR");
  1362. }
  1363. # Since our prototype is output in a pre-formatted block, line up the
  1364. # parameters and parameter comments in the same column.
  1365. # First calculate where the columns should start
  1366. my $biggest_length = 0;
  1367. for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
  1368. {
  1369. my $line = ${$comment->{PROTOTYPE}}[$i];
  1370. if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
  1371. {
  1372. my $length = length $1;
  1373. if ($length > $biggest_length)
  1374. {
  1375. $biggest_length = $length;
  1376. }
  1377. }
  1378. }
  1379. # Now pad the string with blanks
  1380. for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
  1381. {
  1382. my $line = ${$comment->{PROTOTYPE}}[$i];
  1383. if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
  1384. {
  1385. my $pad_len = $biggest_length - length $1;
  1386. my $padding = " " x ($pad_len);
  1387. ${$comment->{PROTOTYPE}}[$i] = $1.$padding.$2;
  1388. }
  1389. }
  1390. for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
  1391. {
  1392. # Format the parameter name
  1393. my $line = ${$comment->{PROTOTYPE}}[$i];
  1394. my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
  1395. $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
  1396. print OUTPUT $line;
  1397. }
  1398. if ($opt_output_format eq "h")
  1399. {
  1400. print OUTPUT " )\n</pre>\n";
  1401. }
  1402. elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
  1403. {
  1404. print OUTPUT " )\n</screen>\n";
  1405. }
  1406. else
  1407. {
  1408. print OUTPUT " )\n";
  1409. }
  1410. output_api_section_end();
  1411. }
  1412. sub output_api_comment($)
  1413. {
  1414. my $comment = shift;
  1415. my $open_paragraph = 0;
  1416. my $open_raw = 0;
  1417. my $param_docs = 0;
  1418. my @fmt;
  1419. if ($opt_output_format eq "h")
  1420. {
  1421. @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
  1422. "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
  1423. "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
  1424. "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
  1425. "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
  1426. }
  1427. elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
  1428. {
  1429. @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
  1430. "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
  1431. "<screen>\n","</screen>\n",
  1432. "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
  1433. "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
  1434. "</entry>","</entry><entry>");
  1435. }
  1436. else
  1437. {
  1438. @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
  1439. "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
  1440. }
  1441. # Extract the parameter names
  1442. my @parameter_names;
  1443. for (@{$comment->{PROTOTYPE}})
  1444. {
  1445. if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
  1446. {
  1447. push (@parameter_names, $2);
  1448. }
  1449. }
  1450. for (@{$comment->{TEXT}})
  1451. {
  1452. if ($opt_output_format eq "h" || $opt_output_format eq "s" || $opt_output_format eq "x")
  1453. {
  1454. # Map special characters
  1455. s/\&/\&amp;/g;
  1456. s/\</\&lt;/g;
  1457. s/\>/\&gt;/g;
  1458. s/\([Cc]\)/\&copy;/g;
  1459. s/\(tm\)/&#174;/;
  1460. }
  1461. if ( s/^\|// )
  1462. {
  1463. # Raw output
  1464. if ($open_raw == 0)
  1465. {
  1466. if ($open_paragraph == 1)
  1467. {
  1468. # Close the open paragraph
  1469. print OUTPUT $fmt[1];
  1470. $open_paragraph = 0;
  1471. }
  1472. # Start raw output
  1473. print OUTPUT $fmt[12];
  1474. $open_raw = 1;
  1475. }
  1476. if ($opt_output_format eq "")
  1477. {
  1478. print OUTPUT ".br\n"; # Prevent 'man' running these lines together
  1479. }
  1480. print OUTPUT $_,"\n";
  1481. }
  1482. else
  1483. {
  1484. if ($opt_output_format eq "h")
  1485. {
  1486. # Link to the file in WineHQ git
  1487. s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 https:\/\/source.winehq.org\/source\/$2/g;
  1488. s/^(Declared in \")(.+?)(\"\.)/$1$2$3 https:\/\/source.winehq.org\/source\/include\/$2/g;
  1489. }
  1490. # Highlight strings
  1491. s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
  1492. # Highlight literal chars
  1493. s/(\'.\')/$fmt[2]$1$fmt[3]/g;
  1494. s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
  1495. # Highlight numeric constants
  1496. s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
  1497. # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
  1498. # FIXME: Using bullet points for leading '-' would look nicer.
  1499. if ($open_paragraph == 1 && $param_docs == 0)
  1500. {
  1501. s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
  1502. s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
  1503. }
  1504. else
  1505. {
  1506. s/^(\-)/$fmt[4]$1$fmt[5]/;
  1507. s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
  1508. }
  1509. if ($opt_output_format eq "h")
  1510. {
  1511. # Html uses links for API calls
  1512. while ( /([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/)
  1513. {
  1514. my $link = $1;
  1515. my $readable_link = $1;
  1516. $readable_link =~ s/-/ /g;
  1517. s/([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/<a href\=\"$link\.html\">$readable_link<\/a>/;
  1518. }
  1519. # Index references
  1520. s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
  1521. s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
  1522. # And references to COM objects (hey, they'll get documented one day)
  1523. s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
  1524. # Convert any web addresses to real links
  1525. s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
  1526. }
  1527. else
  1528. {
  1529. if ($opt_output_format eq "")
  1530. {
  1531. # Give the man section for API calls
  1532. s/ ([A-Za-z_]+[A-Za-z_0-9-]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
  1533. }
  1534. else
  1535. {
  1536. # Highlight API calls
  1537. s/ ([A-Za-z_]+[A-Za-z_0-9-]+\(\))/ $fmt[6]$1$fmt[7]/g;
  1538. }
  1539. # And references to COM objects
  1540. s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
  1541. }
  1542. if ($open_raw == 1)
  1543. {
  1544. # Finish the raw output
  1545. print OUTPUT $fmt[13];
  1546. $open_raw = 0;
  1547. }
  1548. if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
  1549. {
  1550. # Start of a new section
  1551. if ($open_paragraph == 1)
  1552. {
  1553. if ($param_docs == 1)
  1554. {
  1555. print OUTPUT $fmt[17],$fmt[15];
  1556. $param_docs = 0;
  1557. }
  1558. else
  1559. {
  1560. print OUTPUT $fmt[1];
  1561. }
  1562. $open_paragraph = 0;
  1563. }
  1564. output_api_section_start($comment,$_);
  1565. if ( /^PARAMS$/ || /^MEMBERS$/ )
  1566. {
  1567. print OUTPUT $fmt[14];
  1568. $param_docs = 1;
  1569. }
  1570. else
  1571. {
  1572. #print OUTPUT $fmt[15];
  1573. #$param_docs = 0;
  1574. }
  1575. }
  1576. elsif ( /^$/ )
  1577. {
  1578. # Empty line, indicating a new paragraph
  1579. if ($open_paragraph == 1)
  1580. {
  1581. if ($param_docs == 0)
  1582. {
  1583. print OUTPUT $fmt[1];
  1584. $open_paragraph = 0;
  1585. }
  1586. }
  1587. }
  1588. else
  1589. {
  1590. if ($param_docs == 1)
  1591. {
  1592. if ($open_paragraph == 1)
  1593. {
  1594. # For parameter docs, put each parameter into a new paragraph/table row
  1595. print OUTPUT $fmt[17];
  1596. $open_paragraph = 0;
  1597. }
  1598. s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
  1599. }
  1600. else
  1601. {
  1602. # Within paragraph lines, prevent lines running together
  1603. $_ = $_." ";
  1604. }
  1605. # Format parameter names where they appear in the comment
  1606. for my $parameter_name (@parameter_names)
  1607. {
  1608. s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\/]|(\=[^"]))/$1$fmt[8]$2$fmt[9]$3/g;
  1609. }
  1610. # Structure dereferences include the dereferenced member
  1611. s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
  1612. s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
  1613. if ($open_paragraph == 0)
  1614. {
  1615. if ($param_docs == 1)
  1616. {
  1617. print OUTPUT $fmt[16];
  1618. }
  1619. else
  1620. {
  1621. print OUTPUT $fmt[0];
  1622. }
  1623. $open_paragraph = 1;
  1624. }
  1625. # Anything in all uppercase on its own gets emphasised
  1626. s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
  1627. print OUTPUT $_;
  1628. }
  1629. }
  1630. }
  1631. if ($open_raw == 1)
  1632. {
  1633. print OUTPUT $fmt[13];
  1634. }
  1635. if ($param_docs == 1 && $open_paragraph == 1)
  1636. {
  1637. print OUTPUT $fmt[17];
  1638. $open_paragraph = 0;
  1639. }
  1640. if ($param_docs == 1)
  1641. {
  1642. print OUTPUT $fmt[15];
  1643. }
  1644. if ($open_paragraph == 1)
  1645. {
  1646. print OUTPUT $fmt[1];
  1647. }
  1648. }
  1649. # Create the master index file
  1650. sub output_master_index_files()
  1651. {
  1652. if ($opt_output_format eq "")
  1653. {
  1654. return; # No master index for man pages
  1655. }
  1656. if ($opt_output_format eq "h")
  1657. {
  1658. # Append the index entries to the output db of index entries
  1659. my $output_file = $opt_output_directory."/index.db";
  1660. open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
  1661. for (@index_entries_list)
  1662. {
  1663. $_ =~ s/A\,/\,/;
  1664. print INDEXDB $_."\n";
  1665. }
  1666. close(INDEXDB);
  1667. }
  1668. # Use the comment output functions for consistency
  1669. my $comment =
  1670. {
  1671. FILE => "",
  1672. COMMENT_NAME => "The Wine API Guide",
  1673. ALT_NAME => "The Wine API Guide",
  1674. DLL_NAME => "",
  1675. ORDINAL => "",
  1676. RETURNS => "",
  1677. PROTOTYPE => [],
  1678. TEXT => [],
  1679. };
  1680. if ($opt_output_format eq "s" || $opt_output_format eq "x")
  1681. {
  1682. $comment->{COMMENT_NAME} = "Introduction";
  1683. $comment->{ALT_NAME} = "Introduction",
  1684. }
  1685. elsif ($opt_output_format eq "h")
  1686. {
  1687. @{$comment->{TEXT}} = (
  1688. "NAME",
  1689. $comment->{COMMENT_NAME},
  1690. "INTRODUCTION",
  1691. );
  1692. }
  1693. # Create the initial comment text
  1694. push (@{$comment->{TEXT}},
  1695. "This document describes the API calls made available",
  1696. "by Wine. They are grouped by the dll that exports them.",
  1697. "",
  1698. "Please do not edit this document, since it is generated automatically",
  1699. "from the Wine source code tree. Details on updating this documentation",
  1700. "are given in the \"Wine Developers Guide\".",
  1701. "CONTRIBUTORS",
  1702. "API documentation is generally written by the person who ",
  1703. "implements a given API call. Authors of each dll are listed in the overview ",
  1704. "section for that dll. Additional contributors who have updated source files ",
  1705. "but have not entered their names in a copyright statement are noted by an ",
  1706. "entry in the git commit logs.",
  1707. ""
  1708. );
  1709. # Read in all dlls from the database of dll names
  1710. my $input_file = $opt_output_directory."/dlls.db";
  1711. my @dlls = `cat $input_file|sort|uniq`;
  1712. if ($opt_output_format eq "h")
  1713. {
  1714. # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
  1715. push (@{$comment->{TEXT}},
  1716. "INDEX",
  1717. "For an alphabetical listing of the functions available, please click the ",
  1718. "first letter of the functions name below:","",
  1719. "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
  1720. "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
  1721. "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
  1722. "DLLS",
  1723. "Each dll provided by Wine is documented individually. The following dlls are provided :",
  1724. ""
  1725. );
  1726. # Add the dlls to the comment
  1727. for (@dlls)
  1728. {
  1729. $_ =~ s/(\..*)?\n/\(\)/;
  1730. push (@{$comment->{TEXT}}, $_, "");
  1731. }
  1732. output_open_api_file("index");
  1733. }
  1734. elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
  1735. {
  1736. # Just write this as the initial blurb, with a chapter heading
  1737. output_open_api_file("blurb");
  1738. print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine API Guide</title>\n"
  1739. }
  1740. # Write out the document
  1741. output_api_header($comment);
  1742. output_api_comment($comment);
  1743. output_api_footer($comment);
  1744. if ($opt_output_format eq "s" || $opt_output_format eq "x")
  1745. {
  1746. print OUTPUT "</chapter>\n" # finish the chapter
  1747. }
  1748. output_close_api_file();
  1749. if ($opt_output_format eq "s")
  1750. {
  1751. output_sgml_master_file(\@dlls);
  1752. return;
  1753. }
  1754. if ($opt_output_format eq "x")
  1755. {
  1756. output_xml_master_file(\@dlls);
  1757. return;
  1758. }
  1759. if ($opt_output_format eq "h")
  1760. {
  1761. output_html_index_files();
  1762. output_html_stylesheet();
  1763. return;
  1764. }
  1765. }
  1766. # Write the master wine-api.xml, linking it to each dll.
  1767. sub output_xml_master_file($)
  1768. {
  1769. my $dlls = shift;
  1770. output_open_api_file("wine-api");
  1771. print OUTPUT "<?xml version='1.0'?>";
  1772. print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
  1773. print OUTPUT "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook V5.0/EN\" ";
  1774. print OUTPUT " \"http://www.docbook.org/xml/5.0/dtd/docbook.dtd\" [\n\n";
  1775. print OUTPUT "<!ENTITY blurb SYSTEM \"blurb.xml\">\n";
  1776. # List the entities
  1777. for (@$dlls)
  1778. {
  1779. $_ =~ s/(\..*)?\n//;
  1780. print OUTPUT "<!ENTITY ",$_," SYSTEM \"",$_,".xml\">\n"
  1781. }
  1782. print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine API Guide</title></bookinfo>\n\n";
  1783. print OUTPUT " &blurb;\n";
  1784. for (@$dlls)
  1785. {
  1786. print OUTPUT " &",$_,";\n"
  1787. }
  1788. print OUTPUT "\n\n</book>\n";
  1789. output_close_api_file();
  1790. }
  1791. # Write the master wine-api.sgml, linking it to each dll.
  1792. sub output_sgml_master_file($)
  1793. {
  1794. my $dlls = shift;
  1795. output_open_api_file("wine-api");
  1796. print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
  1797. print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
  1798. print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
  1799. # List the entities
  1800. for (@$dlls)
  1801. {
  1802. $_ =~ s/(\..*)?\n//;
  1803. print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
  1804. }
  1805. print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine API Guide</title></bookinfo>\n\n";
  1806. print OUTPUT " &blurb;\n";
  1807. for (@$dlls)
  1808. {
  1809. print OUTPUT " &",$_,";\n"
  1810. }
  1811. print OUTPUT "\n\n</book>\n";
  1812. output_close_api_file();
  1813. }
  1814. # Produce the sgml for the dll chapter from the generated files
  1815. sub output_sgml_dll_file($)
  1816. {
  1817. my $spec_details = shift;
  1818. # Make a list of all the documentation files to include
  1819. my $exports = $spec_details->{EXPORTS};
  1820. my @source_files = ();
  1821. for (@$exports)
  1822. {
  1823. # @$_ => ordinal, call convention, exported name, implementation name, documented;
  1824. if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
  1825. @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
  1826. @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
  1827. @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
  1828. {
  1829. # A documented function
  1830. push (@source_files,@$_[$EXPORT_IMPNAME]);
  1831. }
  1832. }
  1833. push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
  1834. @source_files = sort @source_files;
  1835. # create a new chapter for this dll
  1836. my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
  1837. open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
  1838. print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
  1839. output_close_api_file();
  1840. # Add the sorted documentation, cleaning up as we go
  1841. `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
  1842. for (@source_files)
  1843. {
  1844. `cat $opt_output_directory/$_.sgml >>$tmp_name`;
  1845. `rm -f $opt_output_directory/$_.sgml`;
  1846. }
  1847. # close the chapter, and overwrite the dll source
  1848. open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
  1849. print OUTPUT "</chapter>\n";
  1850. close OUTPUT;
  1851. `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
  1852. }
  1853. # Produce the xml for the dll chapter from the generated files
  1854. sub output_xml_dll_file($)
  1855. {
  1856. my $spec_details = shift;
  1857. # Make a list of all the documentation files to include
  1858. my $exports = $spec_details->{EXPORTS};
  1859. my @source_files = ();
  1860. for (@$exports)
  1861. {
  1862. # @$_ => ordinal, call convention, exported name, implementation name, documented;
  1863. if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
  1864. @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
  1865. @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
  1866. @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
  1867. {
  1868. # A documented function
  1869. push (@source_files,@$_[$EXPORT_IMPNAME]);
  1870. }
  1871. }
  1872. push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
  1873. @source_files = sort @source_files;
  1874. # create a new chapter for this dll
  1875. my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
  1876. open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
  1877. print OUTPUT "<?xml version='1.0' encoding='UTF-8'?>\n<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
  1878. output_close_api_file();
  1879. # Add the sorted documentation, cleaning up as we go
  1880. `cat $opt_output_directory/$spec_details->{DLL_NAME}.xml >>$tmp_name`;
  1881. for (@source_files)
  1882. {
  1883. `cat $opt_output_directory/$_.xml >>$tmp_name`;
  1884. `rm -f $opt_output_directory/$_.xml`;
  1885. }
  1886. # close the chapter, and overwrite the dll source
  1887. open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
  1888. print OUTPUT "</chapter>\n";
  1889. close OUTPUT;
  1890. `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.xml`;
  1891. }
  1892. # Write the html index files containing the function names
  1893. sub output_html_index_files()
  1894. {
  1895. if ($opt_output_format ne "h")
  1896. {
  1897. return;
  1898. }
  1899. my @letters = ('_', 'A' .. 'Z');
  1900. # Read in all functions
  1901. my $input_file = $opt_output_directory."/index.db";
  1902. my @funcs = `cat $input_file|sort|uniq`;
  1903. for (@letters)
  1904. {
  1905. my $letter = $_;
  1906. my $comment =
  1907. {
  1908. FILE => "",
  1909. COMMENT_NAME => "",
  1910. ALT_NAME => "",
  1911. DLL_NAME => "",
  1912. ORDINAL => "",
  1913. RETURNS => "",
  1914. PROTOTYPE => [],
  1915. TEXT => [],
  1916. };
  1917. $comment->{COMMENT_NAME} = $letter." Functions";
  1918. $comment->{ALT_NAME} = $letter." Functions";
  1919. push (@{$comment->{TEXT}},
  1920. "NAME",
  1921. $comment->{COMMENT_NAME},
  1922. "FUNCTIONS"
  1923. );
  1924. # Add the functions to the comment
  1925. for (@funcs)
  1926. {
  1927. my $first_char = substr ($_, 0, 1);
  1928. $first_char = uc $first_char;
  1929. if ($first_char eq $letter)
  1930. {
  1931. my $name = $_;
  1932. my $file;
  1933. $name =~ s/(^.*?)\,(.*?)\n/$1/;
  1934. $file = $2;
  1935. push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
  1936. }
  1937. }
  1938. # Write out the document
  1939. output_open_api_file($letter);
  1940. output_api_header($comment);
  1941. output_api_comment($comment);
  1942. output_api_footer($comment);
  1943. output_close_api_file();
  1944. }
  1945. }
  1946. # Output the stylesheet for HTML output
  1947. sub output_html_stylesheet()
  1948. {
  1949. if ($opt_output_format ne "h")
  1950. {
  1951. return;
  1952. }
  1953. my $css;
  1954. ($css = <<HERE_TARGET) =~ s/^\s+//gm;
  1955. /*
  1956. * Default styles for Wine HTML Documentation.
  1957. *
  1958. * This style sheet should be altered to suit your needs/taste.
  1959. */
  1960. BODY { /* Page body */
  1961. background-color: white;
  1962. color: black;
  1963. font-family: Tahoma,sans-serif;
  1964. font-style: normal;
  1965. font-size: 10pt;
  1966. }
  1967. a:link { color: #4444ff; } /* Links */
  1968. a:visited { color: #333377 }
  1969. a:active { color: #0000dd }
  1970. H2.section { /* Section Headers */
  1971. font-family: sans-serif;
  1972. color: #777777;
  1973. background-color: #F0F0FE;
  1974. margin-left: 0.2in;
  1975. margin-right: 1.0in;
  1976. }
  1977. b.func_name { /* Function Name */
  1978. font-size: 10pt;
  1979. font-style: bold;
  1980. }
  1981. i.dll_ord { /* Italicised DLL+ordinal */
  1982. color: #888888;
  1983. font-family: sans-serif;
  1984. font-size: 8pt;
  1985. }
  1986. p { /* Paragraphs */
  1987. margin-left: 0.5in;
  1988. margin-right: 0.5in;
  1989. }
  1990. table { /* tables */
  1991. margin-left: 0.5in;
  1992. margin-right: 0.5in;
  1993. }
  1994. pre.proto /* API Function prototype */
  1995. {
  1996. border-style: solid;
  1997. border-width: 1px;
  1998. border-color: #777777;
  1999. background-color: #F0F0BB;
  2000. color: black;
  2001. font-size: 10pt;
  2002. vertical-align: top;
  2003. margin-left: 0.5in;
  2004. margin-right: 1.0in;
  2005. }
  2006. pre.raw { /* Raw text output */
  2007. margin-left: 0.6in;
  2008. margin-right: 1.1in;
  2009. background-color: #8080DC;
  2010. }
  2011. tt.param { /* Parameter name */
  2012. font-style: italic;
  2013. color: blue;
  2014. }
  2015. tt.const { /* Constant */
  2016. color: red;
  2017. }
  2018. i.in_out { /* In/Out */
  2019. font-size: 8pt;
  2020. color: grey;
  2021. }
  2022. tt.coderef { /* Code in description text */
  2023. color: darkgreen;
  2024. }
  2025. b.emp /* Emphasis */ {
  2026. font-style: bold;
  2027. color: darkblue;
  2028. }
  2029. i.footer { /* Footer */
  2030. font-family: sans-serif;
  2031. font-size: 6pt;
  2032. color: darkgrey;
  2033. }
  2034. HERE_TARGET
  2035. my $output_file = "$opt_output_directory/apidoc.css";
  2036. open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
  2037. print CSS $css;
  2038. close(CSS);
  2039. }
  2040. sub usage()
  2041. {
  2042. print "\nCreate API Documentation from Wine source code.\n\n",
  2043. "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
  2044. "Where: <spec> is a .spec file giving a DLL's exports.\n",
  2045. " <include> is an include directory used by the DLL.\n",
  2046. " <source> is a source file of the DLL.\n",
  2047. " The above can be given multiple times on the command line, as appropriate.\n",
  2048. "Options:\n",
  2049. " -Th : Output HTML instead of a man page\n",
  2050. " -Ts : Output SGML (DocBook source) instead of a man page\n",
  2051. " -C <dir> : Source directory, to find source files if they are not found in the\n",
  2052. " current directory. Default is \"",$opt_source_dir,"\"\n",
  2053. " -P <dir> : Parent source directory.\n",
  2054. " -R <dir> : Root of build directory.\n",
  2055. " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
  2056. " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
  2057. " -e : Output \"FIXME\" documentation from empty comments.\n",
  2058. " -v : Verbosity. Can be given more than once for more detail.\n";
  2059. }
  2060. #
  2061. # Main
  2062. #
  2063. # Print usage if we're called with no args
  2064. if( @ARGV == 0)
  2065. {
  2066. usage();
  2067. }
  2068. # Process command line options
  2069. while(defined($_ = shift @ARGV))
  2070. {
  2071. if( s/^-// )
  2072. {
  2073. # An option.
  2074. for ($_)
  2075. {
  2076. /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
  2077. s/^S// && do { $opt_manual_section = $_; last; };
  2078. /^Th$/ && do { $opt_output_format = "h"; last; };
  2079. /^Ts$/ && do { $opt_output_format = "s"; last; };
  2080. /^Tx$/ && do { $opt_output_format = "x"; last; };
  2081. /^v$/ && do { $opt_verbose++; last; };
  2082. /^e$/ && do { $opt_output_empty = 1; last; };
  2083. /^L$/ && do { last; };
  2084. /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
  2085. s/^I// && do { if ($_ ne ".") {
  2086. foreach my $include (`find $_/./ -type d ! -name tests`) {
  2087. $include =~ s/\n//;
  2088. $include = $include."/*.h";
  2089. $include =~ s/\/\//\//g;
  2090. my $have_headers = `ls $include >/dev/null 2>&1`;
  2091. if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
  2092. };
  2093. }
  2094. last;
  2095. };
  2096. s/^C// && do {
  2097. if ($_ ne "") { $opt_source_dir = $_; }
  2098. last;
  2099. };
  2100. s/^P// && do {
  2101. if ($_ ne "") { $opt_parent_dir = $_; }
  2102. last;
  2103. };
  2104. s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
  2105. else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
  2106. $opt_wine_root_dir =~ s/\n//;
  2107. $opt_wine_root_dir =~ s/\/\//\//g;
  2108. if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
  2109. last;
  2110. };
  2111. die "Unrecognised option $_\n";
  2112. }
  2113. }
  2114. else
  2115. {
  2116. # A source file.
  2117. push (@opt_source_file_list, $_);
  2118. }
  2119. }
  2120. # Remove duplicate include directories
  2121. my %htmp;
  2122. @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
  2123. if ($opt_verbose > 3)
  2124. {
  2125. print "Output dir:'".$opt_output_directory."'\n";
  2126. print "Section :'".$opt_manual_section."'\n";
  2127. print "Format :'".$opt_output_format."'\n";
  2128. print "Source dir:'".$opt_source_dir."'\n";
  2129. print "Root :'".$opt_wine_root_dir."'\n";
  2130. print "Spec files:'@opt_spec_file_list'\n";
  2131. print "Includes :'@opt_header_file_list'\n";
  2132. print "Sources :'@opt_source_file_list'\n";
  2133. }
  2134. if (@opt_spec_file_list == 0)
  2135. {
  2136. exit 0; # Don't bother processing non-dll files
  2137. }
  2138. # Make sure the output directory exists
  2139. unless (-d $opt_output_directory)
  2140. {
  2141. mkdir $opt_output_directory or die "Cannot create directory $opt_output_directory\n";
  2142. }
  2143. # Read in each .spec files exports and other details
  2144. while(my $spec_file = shift @opt_spec_file_list)
  2145. {
  2146. process_spec_file($spec_file);
  2147. }
  2148. if ($opt_verbose > 3)
  2149. {
  2150. foreach my $spec_file ( keys %spec_files )
  2151. {
  2152. print "in '$spec_file':\n";
  2153. my $spec_details = $spec_files{$spec_file}[0];
  2154. my $exports = $spec_details->{EXPORTS};
  2155. for (@$exports)
  2156. {
  2157. print @$_[$EXPORT_ORDINAL].",".@$_[$EXPORT_CALL].", ".
  2158. @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]."\n";
  2159. }
  2160. }
  2161. }
  2162. # Extract and output the comments from each source file
  2163. while(defined($_ = shift @opt_source_file_list))
  2164. {
  2165. process_source_file($_);
  2166. }
  2167. # Write the index files for each spec
  2168. process_index_files();
  2169. # Write the master index file
  2170. output_master_index_files();
  2171. exit 0;