Authors: Mark Westcott <mark@houseoffish.org>, Mike Hearn <mike@theoretic.com>
[wine/multimedia.git] / tools / c2man.pl
blob5798ae2f95206fba66581edd15387c6e5f143386
1 #! /usr/bin/perl -w
3 # Generate API documentation. See documentation/documentation.sgml for details.
5 # Copyright (C) 2000 Mike McCormack
6 # Copyright (C) 2003 Jon Griffiths
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 # TODO
23 # SGML gurus - feel free to smarten up the SGML.
24 # Add any other relevant information for the dll - imports etc
25 # Should we have a special output mode for WineHQ?
27 use strict;
28 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
39 # Options
40 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
41 my $opt_manual_section = "3w";
42 my $opt_wine_root_dir = "";
43 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml
44 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
45 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
46 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
47 my @opt_header_file_list = ();
48 my @opt_spec_file_list = ();
49 my @opt_source_file_list = ();
51 # All the collected details about all the .spec files being processed
52 my %spec_files;
53 # All the collected details about all the source files being processed
54 my %source_files;
55 # All documented functions that are to be placed in the index
56 my @index_entries_list = ();
58 # useful globals
59 my $pwd = `pwd`."/";
60 $pwd =~ s/\n//;
61 my @datetime = localtime;
62 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
63 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
64 my $year = $datetime[5] + 1900;
65 my $date = "$months[$datetime[4]] $year";
67 sub usage
69 print "\nCreate API Documentation from Wine source code.\n\n",
70 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
71 "Where: <spec> is a .spec file giving a DLL's exports.\n",
72 " <include> is an include directory used by the DLL.\n",
73 " <source> is a source file of the DLL.\n",
74 " The above can be given multiple times on the command line, as appropriate.\n",
75 "Options:\n",
76 " -Th : Output HTML instead of a man page\n",
77 " -Ts : Output SGML (Docbook source) instead of a man page\n",
78 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
79 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
80 " -e : Output \"FIXME\" documentation from empty comments.\n",
81 " -v : Verbosity. Can be given more than once for more detail.\n";
84 # Print usage if we're called with no args
85 if( @ARGV == 0)
87 usage();
90 # Process command line options
91 while(defined($_ = shift @ARGV))
93 if( s/^-// )
95 # An option.
96 for ($_)
98 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
99 s/^S// && do { $opt_manual_section = $_; last; };
100 /^Th$/ && do { $opt_output_format = "h"; last; };
101 /^Ts$/ && do { $opt_output_format = "s"; last; };
102 /^v$/ && do { $opt_verbose++; last; };
103 /^e$/ && do { $opt_output_empty = 1; last; };
104 /^L$/ && do { last; };
105 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
106 s/^I// && do { if ($_ ne ".") {
107 my $include = $_."/*.h";
108 $include =~ s/\/\//\//g;
109 my $have_headers = `ls $include >/dev/null 2>&1`;
110 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
112 last;
114 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
115 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
116 $opt_wine_root_dir =~ s/\n//;
117 $opt_wine_root_dir =~ s/\/\//\//g;
118 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
119 last;
121 die "Unrecognised option $_\n";
124 else
126 # A source file.
127 push (@opt_source_file_list, $_);
131 # Remove duplicate include directories
132 my %htmp;
133 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
135 if ($opt_verbose > 3)
137 print "Output dir:'".$opt_output_directory."'\n";
138 print "Section :'".$opt_manual_section."'\n";
139 print "Format :'".$opt_output_format."'\n";
140 print "Root :'".$opt_wine_root_dir."'\n";
141 print "Spec files:'@opt_spec_file_list'\n";
142 print "Includes :'@opt_header_file_list'\n";
143 print "Sources :'@opt_source_file_list'\n";
146 if (@opt_spec_file_list == 0)
148 exit 0; # Don't bother processing non-dll files
151 # Read in each .spec files exports and other details
152 while(my $spec_file = shift @opt_spec_file_list)
154 process_spec_file($spec_file);
157 if ($opt_verbose > 3)
159 foreach my $spec_file ( keys %spec_files )
161 print "in '$spec_file':\n";
162 my $spec_details = $spec_files{$spec_file}[0];
163 my $exports = $spec_details->{EXPORTS};
164 for (@$exports)
166 print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n";
171 # Extract and output the comments from each source file
172 while(defined($_ = shift @opt_source_file_list))
174 process_source_file($_);
177 # Write the index files for each spec
178 process_index_files();
180 # Write the master index file
181 output_master_index_files();
183 exit 0;
186 # Generate the list of exported entries for the dll
187 sub process_spec_file
189 my $spec_name = shift(@_);
190 my $dll_name = $spec_name;
191 $dll_name =~ s/\..*//; # Strip the file extension
192 my $uc_dll_name = uc $dll_name;
194 my $spec_details =
196 NAME => $spec_name,
197 DLL_NAME => $dll_name,
198 NUM_EXPORTS => 0,
199 NUM_STUBS => 0,
200 NUM_FUNCS => 0,
201 NUM_FORWARDS => 0,
202 NUM_VARS => 0,
203 NUM_DOCS => 0,
204 CONTRIBUTORS => [ ],
205 SOURCES => [ ],
206 DESCRIPTION => [ ],
207 EXPORTS => [ ],
208 EXPORTED_NAMES => { },
209 IMPLEMENTATION_NAMES => { },
210 EXTRA_COMMENTS => [ ],
211 CURRENT_EXTRA => [ ] ,
214 if ($opt_verbose > 0)
216 print "Processing ".$spec_name."\n";
219 # We allow opening to fail just to cater for the peculiarities of
220 # the Wine build system. This doesn't hurt, in any case
221 open(SPEC_FILE, "<$spec_name") || return;
223 while(<SPEC_FILE>)
225 s/^\s+//; # Strip leading space
226 s/\s+\n$/\n/; # Strip trailing space
227 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
228 s/\s*#.*//; # Strip comments
229 s/\(.*\)/ /; # Strip arguments
230 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
231 s/\n$//; # Strip newline
233 my $flags = 0;
234 if( /\-noname/ )
236 $flags |= $FLAG_NONAME;
238 if( /\-i386/ )
240 $flags |= $FLAG_I386;
242 if( /\-register/ )
244 $flags |= $FLAG_REGISTER;
246 s/ \-[a-z0-9]+//g; # Strip flags
248 if( /^(([0-9]+)|@) / )
250 # This line contains an exported symbol
251 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
253 for ($call_convention)
255 /^(cdecl|stdcall|varargs|pascal|pascal16)$/
256 && do { $spec_details->{NUM_FUNCS}++; last; };
257 /^(variable|equate)$/
258 && do { $spec_details->{NUM_VARS}++; last; };
259 /^(forward|extern)$/
260 && do { $spec_details->{NUM_FORWARDS}++; last; };
261 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
262 if ($opt_verbose > 0)
264 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
266 last;
269 # Convert ordinal only names so we can find them later
270 if ($exported_name eq "@")
272 $exported_name = $uc_dll_name.'_'.$ordinal;
274 if (!defined($implementation_name))
276 $implementation_name = $exported_name;
278 if ($implementation_name eq "")
280 $implementation_name = $exported_name;
283 if ($implementation_name =~ /(.*?)\./)
285 $call_convention = "forward"; # Referencing a function from another dll
286 $spec_details->{NUM_FUNCS}--;
287 $spec_details->{NUM_FORWARDS}++;
290 # Add indices for the exported and implementation names
291 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
292 if ($implementation_name ne $exported_name)
294 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
297 # Add the exported entry
298 $spec_details->{NUM_EXPORTS}++;
299 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
300 push (@{$spec_details->{EXPORTS}},[@export]);
303 close(SPEC_FILE);
305 # Add this .spec files details to the list of .spec files
306 $spec_files{$uc_dll_name} = [$spec_details];
309 # Read each source file, extract comments, and generate API documentation if appropriate
310 sub process_source_file
312 my $source_file = shift(@_);
313 my $source_details =
315 CONTRIBUTORS => [ ],
316 DEBUG_CHANNEL => "",
318 my $comment =
320 FILE => $source_file,
321 COMMENT_NAME => "",
322 ALT_NAME => "",
323 DLL_NAME => "",
324 ORDINAL => "",
325 RETURNS => "",
326 PROTOTYPE => [],
327 TEXT => [],
329 my $parse_state = 0;
330 my $ignore_blank_lines = 1;
331 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
333 if ($opt_verbose > 0)
335 print "Processing ".$source_file."\n";
337 open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n";
339 # Add this source file to the list of source files
340 $source_files{$source_file} = [$source_details];
342 while(<SOURCE_FILE>)
344 s/\n$//; # Strip newline
345 s/^\s+//; # Strip leading space
346 s/\s+$//; # Strip trailing space
347 if (! /^\*\|/ )
349 # Strip multiple tabs & spaces to a single space
350 s/\s+/ /g;
353 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
355 # Extract a contributor to this file
356 my $contributor = $2;
357 $contributor =~ s/ *$//;
358 $contributor =~ s/^by //;
359 $contributor =~ s/\.$//;
360 $contributor =~ s/ (for .*)/ \($1\)/;
361 if ($contributor ne "")
363 if ($opt_verbose > 3)
365 print "Info: Found contributor:'".$contributor."'\n";
367 push (@{$source_details->{CONTRIBUTORS}},$contributor);
370 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
372 # Extract the debug channel to use
373 if ($opt_verbose > 3)
375 print "Info: Found debug channel:'".$1."'\n";
377 $source_details->{DEBUG_CHANNEL} = $1;
380 if ($parse_state == 0) # Searching for a comment
382 if ( /^\/\**$/ )
384 # Found a comment start
385 $comment->{COMMENT_NAME} = "";
386 $comment->{ALT_NAME} = "";
387 $comment->{DLL_NAME} = "";
388 $comment->{ORDINAL} = "";
389 $comment->{RETURNS} = "";
390 $comment->{PROTOTYPE} = [];
391 $comment->{TEXT} = [];
392 $ignore_blank_lines = 1;
393 $extra_comment = 0;
394 $parse_state = 3;
397 elsif ($parse_state == 1) # Reading in a comment
399 if ( /^\**\// )
401 # Found the end of the comment
402 $parse_state = 2;
404 elsif ( s/^\*\|/\|/ )
406 # A line of comment not meant to be pre-processed
407 push (@{$comment->{TEXT}},$_); # Add the comment text
409 elsif ( s/^ *\** *// )
411 # A line of comment, starting with an asterisk
412 if ( /^[A-Z]+$/ || $_ eq "")
414 # This is a section start, so skip blank lines before and after it.
415 my $last_line = pop(@{$comment->{TEXT}});
416 if (defined($last_line) && $last_line ne "")
418 # Put it back
419 push (@{$comment->{TEXT}},$last_line);
421 if ( /^[A-Z]+$/ )
423 $ignore_blank_lines = 1;
425 else
427 $ignore_blank_lines = 0;
431 if ($ignore_blank_lines == 0 || $_ ne "")
433 push (@{$comment->{TEXT}},$_); # Add the comment text
436 else
438 # This isn't a well formatted comment: look for the next one
439 $parse_state = 0;
442 elsif ($parse_state == 2) # Finished reading in a comment
444 if ( /(WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
446 # Comment is followed by a function definition
447 $parse_state = 4; # Fall through to read prototype
449 else
451 # Allow cpp directives and blank lines between the comment and prototype
452 if ($extra_comment == 1)
454 # An extra comment not followed by a function definition
455 $parse_state = 5; # Fall through to process comment
457 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
459 # This isn't a well formatted comment: look for the next one
460 if ($opt_verbose > 1)
462 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
464 $parse_state = 0;
468 elsif ($parse_state == 3) # Reading in the first line of a comment
470 s/^ *\** *//;
471 if ( /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
473 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
474 $comment->{COMMENT_NAME} = $1;
475 $comment->{DLL_NAME} = uc $3;
476 $comment->{ORDINAL} = $4;
477 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
478 $parse_state = 1;
480 elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ )
482 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
483 $comment->{COMMENT_NAME} = $1;
484 $comment->{DLL_NAME} = uc $2;
485 $comment->{ORDINAL} = "";
486 $extra_comment = 1;
487 $parse_state = 1;
489 else
491 # This isn't a well formatted comment: look for the next one
492 $parse_state = 0;
496 if ($parse_state == 4) # Reading in the function definition
498 push (@{$comment->{PROTOTYPE}},$_);
499 # Strip comments from the line before checking for ')'
500 my $stripped_line = $_;
501 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
502 if ( $stripped_line =~ /\)/ )
504 # Strip a blank last line
505 my $last_line = pop(@{$comment->{TEXT}});
506 if (defined($last_line) && $last_line ne "")
508 # Put it back
509 push (@{$comment->{TEXT}},$last_line);
512 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
514 # Create a 'not implemented' comment
515 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
517 $parse_state = 5;
521 if ($parse_state == 5) # Processing the comment
523 # Process it, if it has any text
524 if (@{$comment->{TEXT}} > 0)
526 if ($extra_comment == 1)
528 process_extra_comment($comment);
530 else
532 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
533 process_comment($comment);
536 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
538 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
540 $parse_state = 0;
543 close(SOURCE_FILE);
546 # Standardise a comments text for consistency
547 sub process_comment_text
549 my $comment = shift(@_);
550 my $i = 0;
552 for (@{$comment->{TEXT}})
554 if (! /^\|/ )
556 # Map I/O values. These come in too many formats to standardise now....
557 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
558 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
559 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
560 # TRUE/FALSE/NULL are defines, capitilise them
561 s/True|true/TRUE/g;
562 s/False|false/FALSE/g;
563 s/Null|null/NULL/g;
564 # Preferred capitalisations
565 s/ wine| WINE/ Wine/g;
566 s/ API | api / Api /g;
567 s/DLL|Dll/dll /g;
568 s/ URL | url / Url /g;
569 s/WIN16|win16/Win16/g;
570 s/WIN32|win32/Win32/g;
571 s/WIN64|win64/Win64/g;
572 s/ ID | id / Id /g;
573 # Grammar
574 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
575 s/ \:/\:/g; # Colons to the left
576 s/ \;/\;/g; # Semi-colons too
577 # Common idioms
578 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
579 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
580 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
581 # Trademarks
582 s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g;
583 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
584 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
585 s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
586 # Abbreviations
587 s/( char )/ character /g;
588 s/( chars )/ characters /g;
589 s/( info )/ information /g;
590 s/( app )/ application /g;
591 s/( apps )/ applications /g;
592 s/( exe )/ executable /g;
593 s/( ptr )/ pointer /g;
594 s/( obj )/ object /g;
595 s/( err )/ error /g;
596 s/( bool )/ boolean /g;
597 # Punctuation
598 if ( /\[I|\[O/ && ! /\.$/ )
600 $_ = $_."."; # Always have a full stop at the end of parameter desc.
602 elsif ($i > 0 && /^[A-Z]*$/ &&
603 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
604 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
607 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
609 # Paragraphs always end with a full stop
610 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
614 $i++;
618 # Standardise our comment and output it if it is suitable.
619 sub process_comment
621 my $comment = shift(@_);
623 # Don't process this comment if the function isnt exported
624 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
626 if (!defined($spec_details))
628 if ($opt_verbose > 2)
630 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
631 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
633 return;
636 if ($comment->{COMMENT_NAME} eq "@")
638 # Create an implementation name
639 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
642 my $exported_names = $spec_details->{EXPORTED_NAMES};
643 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
644 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
646 if (!defined($export_index))
648 # Perhaps the comment uses the implementation name?
649 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
651 if (!defined($export_index))
653 # This function doesn't appear to be exported. hmm.
654 if ($opt_verbose > 2)
656 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
657 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
659 return;
662 # When the function is exported twice we have the second name below the first
663 # (you see this a lot in ntdll, but also in some other places).
664 my $first_line = ${@{$comment->{TEXT}}}[1];
666 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
668 # Found a second name - mark it as documented
669 my $alt_index = $exported_names->{$1};
670 if (defined($alt_index))
672 if ($opt_verbose > 2)
674 print "Info: Found alternate name '",$1,"\n";
676 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
677 @$alt_export[4] |= $FLAG_DOCUMENTED;
678 $spec_details->{NUM_DOCS}++;
679 ${@{$comment->{TEXT}}}[1] = "";
683 if (@{$spec_details->{CURRENT_EXTRA}})
685 # We have an extra comment that might be related to this one
686 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
687 my $current_name = $current_comment->{COMMENT_NAME};
688 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
690 if ($opt_verbose > 2)
692 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
694 # Add a reference to this comment to our extra comment
695 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
699 # We want our docs generated using the implementation name, so they are unique
700 my $export = @{$spec_details->{EXPORTS}}[$export_index];
701 $comment->{COMMENT_NAME} = @$export[3];
702 $comment->{ALT_NAME} = @$export[2];
704 # Mark the function as documented
705 $spec_details->{NUM_DOCS}++;
706 @$export[4] |= $FLAG_DOCUMENTED;
708 # This file is used by the DLL - Make sure we get our contributors right
709 push (@{$spec_details->{SOURCES}},$comment->{FILE});
711 # If we have parameter comments in the prototype, extract them
712 my @parameter_comments;
713 for (@{$comment->{PROTOTYPE}})
715 s/ *\, */\,/g; # Strip spaces from around commas
717 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
719 my $parameter_comment = $2;
720 if (!$parameter_comment =~ /^\[/ )
722 # Add [IO] markers so we format the comment correctly
723 $parameter_comment = "[fixme] ".$parameter_comment;
725 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
727 # Add the parameter name
728 $parameter_comment = $2." ".$parameter_comment;
730 push (@parameter_comments, $parameter_comment);
734 # If we extracted any prototype comments, add them to the comment text.
735 if (@parameter_comments)
737 @parameter_comments = ("PARAMS", @parameter_comments);
738 my @new_comment = ();
739 my $inserted_params = 0;
741 for (@{$comment->{TEXT}})
743 if ( $inserted_params == 0 && /^[A-Z]+$/ )
745 # Found a section header, so this is where we insert
746 push (@new_comment, @parameter_comments);
747 $inserted_params = 1;
749 push (@new_comment, $_);
751 if ($inserted_params == 0)
753 # Add them to the end
754 push (@new_comment, @parameter_comments);
756 $comment->{TEXT} = [@new_comment];
759 if ($opt_fussy == 1 && $opt_output_empty == 0)
761 # Reject any comment that doesn't have a description or a RETURNS section.
762 # This is the default for now, 'coz many comments aren't suitable.
763 my $found_returns = 0;
764 my $found_description_text = 0;
765 my $in_description = 0;
766 for (@{$comment->{TEXT}})
768 if ( /^RETURNS$/ )
770 $found_returns = 1;
771 $in_description = 0;
773 elsif ( /^DESCRIPTION$/ )
775 $in_description = 1;
777 elsif ($in_description == 1)
779 if ( !/^[A-Z]+$/ )
781 # Dont reject comments that refer to another doc (e.g. A/W)
782 if ( /^See ([A-Za-z0-9_]+)\.$/ )
784 if ($comment->{COMMENT_NAME} =~ /W$/ )
786 # This is probably a Unicode version of an Ascii function.
787 # Create the Ascii name and see if its been documented
788 my $ascii_name = $comment->{COMMENT_NAME};
789 $ascii_name =~ s/W$/A/;
791 my $ascii_export_index = $exported_names->{$ascii_name};
793 if (!defined($ascii_export_index))
795 $ascii_export_index = $implementation_names->{$ascii_name};
797 if (!defined($ascii_export_index))
799 if ($opt_verbose > 2)
801 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
804 else
806 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
807 if (@$ascii_export[4] & $FLAG_DOCUMENTED)
809 # Flag these functions as an A/W pair
810 @$ascii_export[4] |= $FLAG_APAIR;
811 @$export[4] |= $FLAG_WPAIR;
815 $found_returns = 1;
817 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
819 @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
820 $found_returns = 1;
822 $found_description_text = 1;
824 else
826 $in_description = 0;
830 if ($found_returns == 0 || $found_description_text == 0)
832 if ($opt_verbose > 2)
834 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
835 "description and/or RETURNS section, skipping\n";
837 $spec_details->{NUM_DOCS}--;
838 @$export[4] &= ~$FLAG_DOCUMENTED;
839 return;
843 process_comment_text($comment);
845 # Strip the prototypes return value, call convention, name and brackets
846 # (This leaves it as a list of types and names, or empty for void functions)
847 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
848 $prototype =~ s/ / /g;
849 $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
850 $comment->{RETURNS} = $1;
851 $prototype =~ s/ *\).*//; # Strip end bracket
852 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
853 $prototype =~ s/ *\, */\,/g; # Strip space around commas
854 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
855 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
856 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
858 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
860 # Find header file
861 my $h_file = "";
862 if (@$export[4] & $FLAG_NONAME)
864 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
866 else
868 if ($comment->{COMMENT_NAME} ne "")
870 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
871 $tmp = `$tmp`;
872 my $exit_value = $? >> 8;
873 if ($exit_value == 0)
875 $tmp =~ s/\n.*//g;
876 if ($tmp ne "")
878 $h_file = `basename $tmp`;
882 elsif ($comment->{ALT_NAME} ne "")
884 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
885 $tmp = `$tmp`;
886 my $exit_value = $? >> 8;
887 if ($exit_value == 0)
889 $tmp =~ s/\n.*//g;
890 if ($tmp ne "")
892 $h_file = `basename $tmp`;
896 $h_file =~ s/^ *//;
897 $h_file =~ s/\n//;
898 if ($h_file eq "")
900 $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine."
902 else
904 $h_file = "Defined in \"".$h_file."\".";
908 # Find source file
909 my $c_file = $comment->{FILE};
910 if ($opt_wine_root_dir ne "")
912 my $cfile = $pwd."/".$c_file; # Current dir + file
913 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
914 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
915 $cfile =~ s/\n//; # Strip newline
916 my $newfile = $c_file;
917 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
918 $cfile = $cfile."/".$newfile; # Append filename to base path
919 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
920 $cfile =~ s/\/\//\//g; # Remove any double slashes
921 $cfile =~ s/^\/+//; # Strip initial directory slash
922 $c_file = $cfile;
924 $c_file = "Implemented in \"".$c_file."\".";
926 # Add the implementation details
927 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
929 if (@$export[4] & $FLAG_I386)
931 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
933 if (@$export[4] & $FLAG_REGISTER)
935 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
936 "For more details, please read the source code.");
938 my $source_details = $source_files{$comment->{FILE}}[0];
939 if ($source_details->{DEBUG_CHANNEL} ne "")
941 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
944 # Write out the documentation for the API
945 output_comment($comment)
948 # process our extra comment and output it if it is suitable.
949 sub process_extra_comment
951 my $comment = shift(@_);
953 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
955 if (!defined($spec_details))
957 if ($opt_verbose > 2)
959 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
960 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
962 return;
965 # Check first to see if this is documentation for the DLL.
966 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
968 if ($opt_verbose > 2)
970 print "Info: Found DLL documentation\n";
972 for (@{$comment->{TEXT}})
974 push (@{$spec_details->{DESCRIPTION}}, $_);
976 return;
979 # Add the comment to the DLL page as a link
980 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
982 # If we have a prototype, process as a regular comment
983 if (@{$comment->{PROTOTYPE}})
985 $comment->{ORDINAL} = "@";
987 # Add an index for the comment name
988 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
990 # Add a fake exported entry
991 $spec_details->{NUM_EXPORTS}++;
992 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
993 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
994 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
995 push (@{$spec_details->{EXPORTS}},[@export]);
996 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
997 process_comment($comment);
998 return;
1001 if ($opt_verbose > 0)
1003 print "Processing ",$comment->{COMMENT_NAME},"\n";
1006 if (@{$spec_details->{CURRENT_EXTRA}})
1008 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1010 if ($opt_verbose > 0)
1012 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1014 # Output the current comment
1015 process_comment_text($current_comment);
1016 output_open_api_file($current_comment->{COMMENT_NAME});
1017 output_api_header($current_comment);
1018 output_api_name($current_comment);
1019 output_api_comment($current_comment);
1020 output_api_footer($current_comment);
1021 output_close_api_file();
1024 if ($opt_verbose > 2)
1026 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1029 my $comment_copy =
1031 FILE => $comment->{FILE},
1032 COMMENT_NAME => $comment->{COMMENT_NAME},
1033 ALT_NAME => $comment->{ALT_NAME},
1034 DLL_NAME => $comment->{DLL_NAME},
1035 ORDINAL => $comment->{ORDINAL},
1036 RETURNS => $comment->{RETURNS},
1037 PROTOTYPE => [],
1038 TEXT => [],
1041 for (@{$comment->{TEXT}})
1043 push (@{$comment_copy->{TEXT}}, $_);
1045 # Set this comment to be the current extra comment
1046 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1049 # Write a standardised comment out in the appropriate format
1050 sub output_comment
1052 my $comment = shift(@_);
1054 if ($opt_verbose > 0)
1056 print "Processing ",$comment->{COMMENT_NAME},"\n";
1059 if ($opt_verbose > 4)
1061 print "--PROTO--\n";
1062 for (@{$comment->{PROTOTYPE}})
1064 print "'".$_."'\n";
1067 print "--COMMENT--\n";
1068 for (@{$comment->{TEXT} })
1070 print $_."\n";
1074 output_open_api_file($comment->{COMMENT_NAME});
1075 output_api_header($comment);
1076 output_api_name($comment);
1077 output_api_synopsis($comment);
1078 output_api_comment($comment);
1079 output_api_footer($comment);
1080 output_close_api_file();
1083 # Write out an index file for each .spec processed
1084 sub process_index_files
1086 foreach my $spec_file (keys %spec_files)
1088 my $spec_details = $spec_files{$spec_file}[0];
1089 if (defined ($spec_details->{DLL_NAME}))
1091 if (@{$spec_details->{CURRENT_EXTRA}})
1093 # We have an unwritten extra comment, write it
1094 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1095 process_extra_comment($current_comment);
1096 @{$spec_details->{CURRENT_EXTRA}} = ();
1098 output_spec($spec_details);
1103 # Write a spec files documentation out in the appropriate format
1104 sub output_spec
1106 my $spec_details = shift(@_);
1108 if ($opt_verbose > 2)
1110 print "Writing:",$spec_details->{DLL_NAME},"\n";
1113 # Use the comment output functions for consistency
1114 my $comment =
1116 FILE => $spec_details->{DLL_NAME},
1117 COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1118 ALT_NAME => $spec_details->{DLL_NAME},
1119 DLL_NAME => "",
1120 ORDINAL => "",
1121 RETURNS => "",
1122 PROTOTYPE => [],
1123 TEXT => [],
1125 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1126 $spec_details->{NUM_FUNCS};
1127 my $percent_implemented = 0;
1128 if ($total_implemented)
1130 $percent_implemented = $total_implemented /
1131 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1133 $percent_implemented = int($percent_implemented);
1134 my $percent_documented = 0;
1135 if ($spec_details->{NUM_DOCS})
1137 # Treat forwards and data as documented funcs for statistics
1138 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1139 $percent_documented = int($percent_documented);
1142 # Make a list of the contributors to this DLL. Do this only for the source
1143 # files that make up the DLL, because some directories specify multiple dlls.
1144 my @contributors;
1146 for (@{$spec_details->{SOURCES}})
1148 my $source_details = $source_files{$_}[0];
1149 for (@{$source_details->{CONTRIBUTORS}})
1151 push (@contributors, $_);
1155 my %saw;
1156 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1157 @contributors = sort @contributors;
1159 # Remove duplicates and blanks
1160 for(my $i=0; $i<@contributors; $i++)
1162 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1164 $contributors[$i-1] = $contributors[$i];
1167 undef %saw;
1168 @contributors = grep(!$saw{$_}++, @contributors);
1170 if ($opt_verbose > 3)
1172 print "Contributors:\n";
1173 for (@contributors)
1175 print "'".$_."'\n";
1178 my $contribstring = join (", ", @contributors);
1180 # Create the initial comment text
1181 @{$comment->{TEXT}} = (
1182 "NAME",
1183 $comment->{COMMENT_NAME}
1186 # Add the description, if we have one
1187 if (@{$spec_details->{DESCRIPTION}})
1189 push (@{$comment->{TEXT}}, "DESCRIPTION");
1190 for (@{$spec_details->{DESCRIPTION}})
1192 push (@{$comment->{TEXT}}, $_);
1196 # Add the statistics and contributors
1197 push (@{$comment->{TEXT}},
1198 "STATISTICS",
1199 "Forwards: ".$spec_details->{NUM_FORWARDS},
1200 "Variables: ".$spec_details->{NUM_VARS},
1201 "Stubs: ".$spec_details->{NUM_STUBS},
1202 "Functions: ".$spec_details->{NUM_FUNCS},
1203 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1204 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1205 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1206 "CONTRIBUTORS",
1207 "The following people hold copyrights on the source files comprising this dll:",
1209 $contribstring,
1210 "Note: This list may not be complete.",
1211 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1215 if ($opt_output_format eq "h")
1217 # Add the exports to the comment text
1218 push (@{$comment->{TEXT}},"EXPORTS");
1219 my $exports = $spec_details->{EXPORTS};
1220 for (@$exports)
1222 my $line = "";
1224 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1225 if (@$_[1] eq "forward")
1227 my $forward_dll = @$_[3];
1228 $forward_dll =~ s/\.(.*)//;
1229 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1231 elsif (@$_[1] eq "extern")
1233 $line = @$_[2]." (extern)";
1235 elsif (@$_[1] eq "stub")
1237 $line = @$_[2]." (stub)";
1239 elsif (@$_[1] eq "fake")
1241 # Don't add this function here, it gets listed with the extra documentation
1242 if (!(@$_[4] & $FLAG_WPAIR))
1244 # This function should be indexed
1245 push (@index_entries_list, @$_[3].",".@$_[3]);
1248 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1250 $line = @$_[2]." (data)";
1252 else
1254 # A function
1255 if (@$_[4] & $FLAG_DOCUMENTED)
1257 # Documented
1258 $line = @$_[2]." (implemented as ".@$_[3]."())";
1259 if (@$_[2] ne @$_[3])
1261 $line = @$_[2]." (implemented as ".@$_[3]."())";
1263 else
1265 $line = @$_[2]."()";
1267 if (!(@$_[4] & $FLAG_WPAIR))
1269 # This function should be indexed
1270 push (@index_entries_list, @$_[2].",".@$_[3]);
1273 else
1275 $line = @$_[2]." (not documented)";
1278 if ($line ne "")
1280 push (@{$comment->{TEXT}}, $line, "");
1284 # Add links to the extra documentation
1285 if (@{$spec_details->{EXTRA_COMMENTS}})
1287 push (@{$comment->{TEXT}}, "SEE ALSO");
1288 my %htmp;
1289 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1290 for (@{$spec_details->{EXTRA_COMMENTS}})
1292 push (@{$comment->{TEXT}}, $_."()", "");
1296 # The dll entry should also be indexed
1297 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1299 # Write out the document
1300 output_open_api_file($spec_details->{DLL_NAME});
1301 output_api_header($comment);
1302 output_api_comment($comment);
1303 output_api_footer($comment);
1304 output_close_api_file();
1306 # Add this dll to the database of dll names
1307 my $output_file = $opt_output_directory."/dlls.db";
1309 # Append the dllname to the output db of names
1310 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1311 print DLLDB $spec_details->{DLL_NAME},"\n";
1312 close(DLLDB);
1314 if ($opt_output_format eq "s")
1316 output_sgml_dll_file($spec_details);
1317 return;
1322 # OUTPUT FUNCTIONS
1323 # ----------------
1324 # Only these functions know anything about formatting for a specific
1325 # output type. The functions above work only with plain text.
1326 # This is to allow new types of output to be added easily.
1328 # Open the api file
1329 sub output_open_api_file
1331 my $output_name = shift(@_);
1332 $output_name = $opt_output_directory."/".$output_name;
1334 if ($opt_output_format eq "h")
1336 $output_name = $output_name.".html";
1338 elsif ($opt_output_format eq "s")
1340 $output_name = $output_name.".sgml";
1342 else
1344 $output_name = $output_name.".".$opt_manual_section;
1346 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1349 # Close the api file
1350 sub output_close_api_file
1352 close (OUTPUT);
1355 # Output the api file header
1356 sub output_api_header
1358 my $comment = shift(@_);
1360 if ($opt_output_format eq "h")
1362 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1363 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1364 print OUTPUT "<HTML>\n<HEAD>\n";
1365 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1366 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1367 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1368 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1370 elsif ($opt_output_format eq "s")
1372 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1373 "<sect1>\n",
1374 "<title>$comment->{COMMENT_NAME}</title>\n";
1376 else
1378 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1379 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1380 "Wine API\" \"Wine API\"\n";
1384 sub output_api_footer
1386 if ($opt_output_format eq "h")
1388 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1389 " All trademarks are the property of their respective owners.".
1390 " Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1391 " Generated $date.</i></p>\n</body>\n</html>\n";
1393 elsif ($opt_output_format eq "s")
1395 print OUTPUT "</sect1>\n";
1396 return;
1398 else
1403 sub output_api_section_start
1405 my $comment = shift(@_);
1406 my $section_name = shift(@_);
1408 if ($opt_output_format eq "h")
1410 print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1412 elsif ($opt_output_format eq "s")
1414 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1416 else
1418 print OUTPUT "\n\.SH ",$section_name,"\n";
1422 sub output_api_section_end
1424 # Not currently required by any output formats
1427 sub output_api_name
1429 my $comment = shift(@_);
1431 output_api_section_start($comment,"NAME");
1433 my $dll_ordinal = "";
1434 if ($comment->{ORDINAL} ne "")
1436 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1438 if ($opt_output_format eq "h")
1440 print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1441 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1442 ,$dll_ordinal,"</i></p>\n";
1444 elsif ($opt_output_format eq "s")
1446 print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>",
1447 $dll_ordinal,"</emphasis>\n</para>\n";
1449 else
1451 print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1454 output_api_section_end();
1457 sub output_api_synopsis
1459 my $comment = shift(@_);
1460 my @fmt;
1462 output_api_section_start($comment,"SYNOPSIS");
1464 if ($opt_output_format eq "h")
1466 print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1467 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1469 elsif ($opt_output_format eq "s")
1471 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1472 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1474 else
1476 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1477 @fmt = ("", "\n", "\\fI", "\\fR");
1480 # Since our prototype is output in a pre-formatted block, line up the
1481 # parameters and parameter comments in the same column.
1483 # First caluculate where the columns should start
1484 my $biggest_length = 0;
1485 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1487 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1488 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1490 my $length = length $1;
1491 if ($length > $biggest_length)
1493 $biggest_length = $length;
1498 # Now pad the string with blanks
1499 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1501 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1502 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1504 my $pad_len = $biggest_length - length $1;
1505 my $padding = " " x ($pad_len);
1506 ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1510 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1512 # Format the parameter name
1513 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1514 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1515 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1516 print OUTPUT $line;
1519 if ($opt_output_format eq "h")
1521 print OUTPUT " )\n\n</pre></p>\n";
1523 elsif ($opt_output_format eq "s")
1525 print OUTPUT " )\n</screen>\n";
1527 else
1529 print OUTPUT " )\n";
1532 output_api_section_end();
1535 sub output_api_comment
1537 my $comment = shift(@_);
1538 my $open_paragraph = 0;
1539 my $open_raw = 0;
1540 my $param_docs = 0;
1541 my @fmt;
1543 if ($opt_output_format eq "h")
1545 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1546 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1547 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1548 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1549 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1551 elsif ($opt_output_format eq "s")
1553 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1554 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1555 "<screen>\n","</screen>\n",
1556 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1557 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1558 "</entry>","</entry><entry>");
1560 else
1562 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1563 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1566 # Extract the parameter names
1567 my @parameter_names;
1568 for (@{$comment->{PROTOTYPE}})
1570 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1572 push (@parameter_names, $2);
1576 for (@{$comment->{TEXT}})
1578 if ($opt_output_format eq "h" || $opt_output_format eq "s")
1580 # Map special characters
1581 s/\&/\&amp;/g;
1582 s/\</\&lt;/g;
1583 s/\>/\&gt;/g;
1584 s/\([Cc]\)/\&copy;/g;
1585 s/\(tm\)/&#174;/;
1588 if ( s/^\|// )
1590 # Raw output
1591 if ($open_raw == 0)
1593 if ($open_paragraph == 1)
1595 # Close the open paragraph
1596 print OUTPUT $fmt[1];
1597 $open_paragraph = 0;
1599 # Start raw output
1600 print OUTPUT $fmt[12];
1601 $open_raw = 1;
1603 if ($opt_output_format eq "")
1605 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1607 print OUTPUT $_,"\n";
1609 else
1611 # Highlight strings
1612 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1613 # Highlight literal chars
1614 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1615 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1616 # Highlight numeric constants
1617 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1619 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1620 # FIXME: Using bullet points for leading '-' would look nicer.
1621 if ($open_paragraph == 1)
1623 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1624 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1626 else
1628 s/^(\-)/$fmt[4]$1$fmt[5]/;
1629 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1632 if ($opt_output_format eq "h")
1634 # Html uses links for API calls
1635 s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1636 # Index references
1637 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1638 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1639 # And references to COM objects (hey, they'll get documented one day)
1640 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1641 # Convert any web addresses to real links
1642 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1644 else
1646 if ($opt_output_format eq "")
1648 # Give the man section for API calls
1649 s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1651 else
1653 # Highlight API calls
1654 s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1657 # And references to COM objects
1658 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1661 if ($open_raw == 1)
1663 # Finish the raw output
1664 print OUTPUT $fmt[13];
1665 $open_raw = 0;
1668 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1670 # Start of a new section
1671 if ($open_paragraph == 1)
1673 if ($param_docs == 1)
1675 print OUTPUT $fmt[17],$fmt[15];
1677 else
1679 print OUTPUT $fmt[1];
1681 $open_paragraph = 0;
1683 output_api_section_start($comment,$_);
1684 if ( /^PARAMS$/ )
1686 print OUTPUT $fmt[14];
1687 $param_docs = 1;
1689 else
1691 #print OUTPUT $fmt[15];
1692 $param_docs = 0;
1695 elsif ( /^$/ )
1697 # Empty line, indicating a new paragraph
1698 if ($open_paragraph == 1)
1700 if ($param_docs == 0)
1702 print OUTPUT $fmt[1];
1703 $open_paragraph = 0;
1707 else
1709 if ($param_docs == 1)
1711 if ($open_paragraph == 1)
1713 # For parameter docs, put each parameter into a new paragraph/table row
1714 print OUTPUT $fmt[17];
1715 $open_paragraph = 0;
1717 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1719 else
1721 # Within paragraph lines, prevent lines running together
1722 $_ = $_." ";
1725 # Format parameter names where they appear in the comment
1726 for my $parameter_name (@parameter_names)
1728 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\=\/])/$1$fmt[8]$2$fmt[9]$3/g;
1730 # Structure dereferences include the dereferenced member
1731 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1732 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1734 if ($open_paragraph == 0)
1736 if ($param_docs == 1)
1738 print OUTPUT $fmt[16];
1740 else
1742 print OUTPUT $fmt[0];
1744 $open_paragraph = 1;
1746 # Anything in all uppercase on its own gets emphasised
1747 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1749 print OUTPUT $_;
1753 if ($open_raw == 1)
1755 print OUTPUT $fmt[13];
1757 if ($open_paragraph == 1)
1759 print OUTPUT $fmt[1];
1763 # Create the master index file
1764 sub output_master_index_files
1766 if ($opt_output_format eq "")
1768 return; # No master index for man pages
1771 if ($opt_output_format eq "h")
1773 # Append the index entries to the output db of index entries
1774 my $output_file = $opt_output_directory."/index.db";
1775 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1776 for (@index_entries_list)
1778 $_ =~ s/A\,/\,/;
1779 print INDEXDB $_."\n";
1781 close(INDEXDB);
1784 # Use the comment output functions for consistency
1785 my $comment =
1787 FILE => "",
1788 COMMENT_NAME => "The Wine Api Guide",
1789 ALT_NAME => "The Wine Api Guide",
1790 DLL_NAME => "",
1791 ORDINAL => "",
1792 RETURNS => "",
1793 PROTOTYPE => [],
1794 TEXT => [],
1797 if ($opt_output_format eq "s")
1799 $comment->{COMMENT_NAME} = "Introduction";
1800 $comment->{ALT_NAME} = "Introduction",
1802 elsif ($opt_output_format eq "h")
1804 @{$comment->{TEXT}} = (
1805 "NAME",
1806 $comment->{COMMENT_NAME},
1807 "INTRODUCTION",
1811 # Create the initial comment text
1812 push (@{$comment->{TEXT}},
1813 "This document describes the Api calls made available",
1814 "by Wine. They are grouped by the dll that exports them.",
1816 "Please do not edit this document, since it is generated automatically",
1817 "from the Wine source code tree. Details on updating this documentation",
1818 "are given in the \"Wine Developers Guide\".",
1819 "CONTRIBUTORS",
1820 "Api documentation is generally written by the person who ",
1821 "implements a given Api call. Authors of each dll are listed in the overview ",
1822 "section for that dll. Additional contributors who have updated source files ",
1823 "but have not entered their names in a copyright statement are noted by an ",
1824 "entry in the file \"Changelog\" from the Wine source code distribution.",
1828 # Read in all dlls from the database of dll names
1829 my $input_file = $opt_output_directory."/dlls.db";
1830 my @dlls = `cat $input_file|sort|uniq`;
1832 if ($opt_output_format eq "h")
1834 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1835 push (@{$comment->{TEXT}},
1836 "INDEX",
1837 "For an alphabetical listing of the functions available, please click the ",
1838 "first letter of the functions name below:","",
1839 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1840 "I(), J(), K(), L(), M(), N(), O(), P(), Q() ".
1841 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1842 "DLLS",
1843 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1846 # Add the dlls to the comment
1847 for (@dlls)
1849 $_ =~ s/(\..*)?\n/\(\)/;
1850 push (@{$comment->{TEXT}}, $_, "");
1852 output_open_api_file("index");
1854 elsif ($opt_output_format eq "s")
1856 # Just write this as the initial blurb, with a chapter heading
1857 output_open_api_file("blurb");
1858 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1861 # Write out the document
1862 output_api_header($comment);
1863 output_api_comment($comment);
1864 output_api_footer($comment);
1865 if ($opt_output_format eq "s")
1867 print OUTPUT "</chapter>\n" # finish the chapter
1869 output_close_api_file();
1871 if ($opt_output_format eq "s")
1873 output_sgml_master_file(\@dlls);
1874 return;
1876 if ($opt_output_format eq "h")
1878 output_html_index_files();
1879 output_html_stylesheet();
1880 return;
1884 # Write the master wine-api.sgml, linking it to each dll.
1885 sub output_sgml_master_file
1887 my $dlls = shift(@_);
1889 output_open_api_file("wine-api");
1890 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1891 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1892 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1894 # List the entities
1895 for (@$dlls)
1897 $_ =~ s/(\..*)?\n//;
1898 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1901 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1902 print OUTPUT " &blurb;\n";
1904 for (@$dlls)
1906 print OUTPUT " &",$_,";\n"
1908 print OUTPUT "\n\n</book>\n";
1910 output_close_api_file();
1913 # Produce the sgml for the dll chapter from the generated files
1914 sub output_sgml_dll_file
1916 my $spec_details = shift(@_);
1918 # Make a list of all the documentation files to include
1919 my $exports = $spec_details->{EXPORTS};
1920 my @source_files = ();
1921 for (@$exports)
1923 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1924 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1925 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1927 # A documented function
1928 push (@source_files,@$_[3]);
1932 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1934 @source_files = sort @source_files;
1936 # create a new chapter for this dll
1937 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1938 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1939 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1940 output_close_api_file();
1942 # Add the sorted documentation, cleaning up as we go
1943 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1944 for (@source_files)
1946 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1947 `rm -f $opt_output_directory/$_.sgml`;
1950 # close the chapter, and overwite the dll source
1951 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1952 print OUTPUT "</chapter>\n";
1953 close OUTPUT;
1954 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1957 # Write the html index files containing the function names
1958 sub output_html_index_files
1960 if ($opt_output_format ne "h")
1962 return;
1965 my @letters = ('_', 'A' .. 'Z');
1967 # Read in all functions
1968 my $input_file = $opt_output_directory."/index.db";
1969 my @funcs = `cat $input_file|sort|uniq`;
1971 for (@letters)
1973 my $letter = $_;
1974 my $comment =
1976 FILE => "",
1977 COMMENT_NAME => "",
1978 ALT_NAME => "",
1979 DLL_NAME => "",
1980 ORDINAL => "",
1981 RETURNS => "",
1982 PROTOTYPE => [],
1983 TEXT => [],
1986 $comment->{COMMENT_NAME} = $letter." Functions";
1987 $comment->{ALT_NAME} = $letter." Functions";
1989 push (@{$comment->{TEXT}},
1990 "NAME",
1991 $comment->{COMMENT_NAME},
1992 "FUNCTIONS"
1995 # Add the functions to the comment
1996 for (@funcs)
1998 my $first_char = substr ($_, 0, 1);
1999 $first_char = uc $first_char;
2001 if ($first_char eq $letter)
2003 my $name = $_;
2004 my $file;
2005 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2006 $file = $2;
2007 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2011 # Write out the document
2012 output_open_api_file($letter);
2013 output_api_header($comment);
2014 output_api_comment($comment);
2015 output_api_footer($comment);
2016 output_close_api_file();
2020 # Output the stylesheet for HTML output
2021 sub output_html_stylesheet
2023 if ($opt_output_format ne "h")
2025 return;
2028 my $css;
2029 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2031 * Default styles for Wine HTML Documentation.
2033 * This style sheet should be altered to suit your needs/taste.
2035 BODY { /* Page body */
2036 background-color: white;
2037 color: black;
2038 font-family: Tahoma,sans-serif;
2039 font-style: normal;
2040 font-size: 10pt;
2042 a:link { color: #4444ff; } /* Links */
2043 a:visited { color: #333377 }
2044 a:active { color: #0000dd }
2045 H2.section { /* Section Headers */
2046 font-family: sans-serif;
2047 color: #777777;
2048 background-color: #F0F0FE;
2049 margin-left: 0.2in;
2050 margin-right: 1.0in;
2052 b.func_name { /* Function Name */
2053 font-size: 10pt;
2054 font-style: bold;
2056 i.dll_ord { /* Italicised DLL+ordinal */
2057 color: #888888;
2058 font-family: sans-serif;
2059 font-size: 8pt;
2061 p { /* Paragraphs */
2062 margin-left: 0.5in;
2063 margin-right: 0.5in;
2065 table { /* tables */
2066 margin-left: 0.5in;
2067 margin-right: 0.5in;
2069 pre.proto /* API Function prototype */
2071 border-style: solid;
2072 border-width: 1px;
2073 border-color: #777777;
2074 background-color: #F0F0BB;
2075 color: black;
2076 font-size: 10pt;
2077 vertical-align: top;
2078 margin-left: 0.5in;
2079 margin-right: 1.0in;
2081 pre.raw { /* Raw text output */
2082 margin-left: 0.6in;
2083 margin-right: 1.1in;
2084 background-color: #8080DC;
2086 tt.param { /* Parameter name */
2087 font-style: italic;
2088 color: blue;
2090 tt.const { /* Constant */
2091 color: red;
2093 i.in_out { /* In/Out */
2094 font-size: 8pt;
2095 color: grey;
2097 tt.coderef { /* Code in description text */
2098 color: darkgreen;
2100 b.emp /* Emphasis */ {
2101 font-style: bold;
2102 color: darkblue;
2104 i.footer { /* Footer */
2105 font-family: sans-serif;
2106 font-size: 6pt;
2107 color: darkgrey;
2109 HERE_TARGET
2111 my $output_file = "$opt_output_directory/apidoc.css";
2112 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2113 print CSS $css;
2114 close(CSS);