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
23 # SGML gurus - feel free to smarten up the SGML.
24 # Add any other relevant information for the dll - imports etc
25 # Read .spec flags such as -i386,-noname and add to docs appropriately
26 # Generate an alpabetical listing of every API call, with links
27 # Should we have a special output mode for WineHQ?
32 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
33 my $opt_manual_section = "3w";
34 my $opt_wine_root_dir = "";
35 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml
36 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
37 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
38 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
39 my @opt_header_file_list = ();
40 my @opt_spec_file_list = ();
41 my @opt_source_file_list = ();
43 # All the collected details about all the .spec files being processed
45 # All the collected details about all the source files being processed
51 my @datetime = localtime;
52 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
53 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
54 my $year = $datetime[5] + 1900;
55 my $date = "$months[$datetime[4]] $year";
59 print "\nCreate API Documentation from Wine source code.\n\n",
60 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
61 "Where: <spec> is a .spec file giving a DLL's exports.\n",
62 " <include> is an include directory used by the DLL.\n",
63 " <source> is a source file of the DLL.\n",
64 " The above can be given multiple times on the command line, as appropriate.\n",
66 " -Th : Output HTML instead of a man page\n",
67 " -Ts : Output SGML (Docbook source) instead of a man page\n",
68 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
69 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
70 " -e : Output \"FIXME\" documentation from empty comments.\n",
71 " -v : Verbosity. Can be given more than once for more detail.\n";
74 # Print usage if we're called with no args
80 # Process command line options
81 while(defined($_ = shift @ARGV))
88 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
89 s/^S// && do { $opt_manual_section = $_; last; };
90 /^Th$/ && do { $opt_output_format = "h"; last; };
91 /^Ts$/ && do { $opt_output_format = "s"; last; };
92 /^v$/ && do { $opt_verbose++; last; };
93 /^e$/ && do { $opt_output_empty = 1; last; };
94 /^L$/ && do { last; };
95 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
96 s/^I// && do { my $include = $_."/*.h";
97 $include =~ s/\/\//\//g;
98 my $have_headers = `ls $include >/dev/null 2>&1`;
99 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
102 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
103 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
104 $opt_wine_root_dir =~ s/\n//;
105 $opt_wine_root_dir =~ s/\/\//\//g;
106 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
109 die "Unrecognised option $_\n";
115 push (@opt_source_file_list, $_);
119 # Remove duplicate include directories
121 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
123 if ($opt_verbose > 3)
125 print "Output dir:'".$opt_output_directory."'\n";
126 print "Section :'".$opt_manual_section."'\n";
127 print "Format :'".$opt_output_format."'\n";
128 print "Root :'".$opt_wine_root_dir."'\n";
129 print "Spec files:'@opt_spec_file_list'\n";
130 print "Includes :'@opt_header_file_list'\n";
131 print "Sources :'@opt_source_file_list'\n";
134 if (@opt_spec_file_list == 0)
136 exit 0; # Don't bother processing non-dll files
139 # Read in each .spec files exports and other details
140 while(my $spec_file = shift @opt_spec_file_list)
142 process_spec_file($spec_file);
145 if ($opt_verbose > 3)
147 foreach my $spec_file ( keys %spec_files )
149 print "in '$spec_file':\n";
150 my $spec_details = $spec_files{$spec_file}[0];
151 my $exports = $spec_details->{EXPORTS};
154 print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n";
159 # Extract and output the comments from each source file
160 while(defined($_ = shift @opt_source_file_list))
162 process_source_file($_);
165 # Write the index files for each spec
166 process_index_files();
168 # Write the master index file
169 output_master_index_files();
174 # Generate the list of exported entries for the dll
175 sub process_spec_file
177 my $spec_name = shift(@_);
178 my $dll_name = $spec_name;
179 $dll_name =~ s/\..*//; # Strip the file extension
180 my $uc_dll_name = uc $dll_name;
185 DLL_NAME => $dll_name,
196 EXPORTED_NAMES => { },
197 IMPLEMENTATION_NAMES => { },
198 EXTRA_COMMENTS => [ ],
199 CURRENT_EXTRA => [ ] ,
202 if ($opt_verbose > 0)
204 print "Processing ".$spec_name."\n";
207 # We allow opening to fail just to cater for the peculiarities of
208 # the Wine build system. This doesn't hurt, in any case
209 open(SPEC_FILE, "<$spec_name") || return;
213 s/^\s+//; # Strip leading space
214 s/\s+\n$/\n/; # Strip trailing space
215 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
216 s/\s*#.*//; # Strip comments
217 s/\(.*\)/ /; # Strip arguments
218 s/ \-[a-z0-9]+//g; # Strip modifiers
219 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
220 s/\n$//; # Strip newline
222 if( /^(([0-9]+)|@) / )
224 # This line contains an exported symbol
225 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
227 for ($call_convention)
229 /^(cdecl|stdcall|varargs|pascal|pascal16)$/
230 && do { $spec_details->{NUM_FUNCS}++; last; };
231 /^(variable|equate)$/
232 && do { $spec_details->{NUM_VARS}++; last; };
234 && do { $spec_details->{NUM_FORWARDS}++; last; };
235 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
236 if ($opt_verbose > 0)
238 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
243 # Convert ordinal only names so we can find them later
244 if ($exported_name eq "@")
246 $exported_name = $uc_dll_name.'_'.$ordinal;
248 if (!defined($implementation_name))
250 $implementation_name = $exported_name;
252 # Add indices for the exported and implementation names
253 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
254 if ($implementation_name ne $exported_name)
256 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
259 # Add the exported entry
260 $spec_details->{NUM_EXPORTS}++;
262 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
263 push (@{$spec_details->{EXPORTS}},[@export]);
268 # Add this .spec files details to the list of .spec files
269 $spec_files{$uc_dll_name} = [$spec_details];
272 # Read each source file, extract comments, and generate API documentation if appropriate
273 sub process_source_file
275 my $source_file = shift(@_);
283 FILE => $source_file,
293 my $ignore_blank_lines = 1;
294 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
296 if ($opt_verbose > 0)
298 print "Processing ".$source_file."\n";
300 open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n";
302 # Add this source file to the list of source files
303 $source_files{$source_file} = [$source_details];
307 s/\n$//; # Strip newline
308 s/^\s+//; # Strip leading space
309 s/\s+$//; # Strip trailing space
312 # Strip multiple tabs & spaces to a single space
316 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
318 # Extract a contributor to this file
319 my $contributor = $2;
320 $contributor =~ s/ *$//;
321 $contributor =~ s/^by //;
322 $contributor =~ s/\.$//;
323 $contributor =~ s/ (for .*)/ \($1\)/;
324 if ($contributor ne "")
326 if ($opt_verbose > 3)
328 print "Info: Found contributor:'".$contributor."'\n";
330 push (@{$source_details->{CONTRIBUTORS}},$contributor);
333 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
335 # Extract the debug channel to use
336 if ($opt_verbose > 3)
338 print "Info: Found debug channel:'".$1."'\n";
340 $source_details->{DEBUG_CHANNEL} = $1;
343 if ($parse_state == 0) # Searching for a comment
347 # Found a comment start
348 $comment->{COMMENT_NAME} = "";
349 $comment->{ALT_NAME} = "";
350 $comment->{DLL_NAME} = "";
351 $comment->{ORDINAL} = "";
352 $comment->{RETURNS} = "";
353 $comment->{PROTOTYPE} = [];
354 $comment->{TEXT} = [];
355 $ignore_blank_lines = 1;
360 elsif ($parse_state == 1) # Reading in a comment
364 # Found the end of the comment
367 elsif ( s/^\*\|/\|/ )
369 # A line of comment not meant to be pre-processed
370 push (@{$comment->{TEXT}},$_); # Add the comment text
372 elsif ( s/^ *\** *// )
374 # A line of comment, starting with an asterisk
375 if ( /^[A-Z]+$/ || $_ eq "")
377 # This is a section start, so skip blank lines before and after it.
378 my $last_line = pop(@{$comment->{TEXT}});
379 if (defined($last_line) && $last_line ne "")
382 push (@{$comment->{TEXT}},$last_line);
386 $ignore_blank_lines = 1;
390 $ignore_blank_lines = 0;
394 if ($ignore_blank_lines == 0 || $_ ne "")
396 push (@{$comment->{TEXT}},$_); # Add the comment text
401 # This isn't a well formatted comment: look for the next one
405 elsif ($parse_state == 2) # Finished reading in a comment
407 if ( /(WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
409 # Comment is followed by a function definition
410 $parse_state = 4; # Fall through to read prototype
414 # Allow cpp directives and blank lines between the comment and prototype
415 if ($extra_comment == 1)
417 # An extra comment not followed by a function definition
418 $parse_state = 5; # Fall through to process comment
420 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
422 # This isn't a well formatted comment: look for the next one
423 if ($opt_verbose > 1)
425 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
431 elsif ($parse_state == 3) # Reading in the first line of a comment
434 if ( /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
436 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
437 $comment->{COMMENT_NAME} = $1;
438 $comment->{DLL_NAME} = uc $3;
439 $comment->{ORDINAL} = $4;
440 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
443 elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ )
445 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
446 $comment->{COMMENT_NAME} = $1;
447 $comment->{DLL_NAME} = uc $2;
448 $comment->{ORDINAL} = "";
454 # This isn't a well formatted comment: look for the next one
459 if ($parse_state == 4) # Reading in the function definition
461 push (@{$comment->{PROTOTYPE}},$_);
462 # Strip comments from the line before checking for ')'
463 my $stripped_line = $_;
464 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
465 if ( $stripped_line =~ /\)/ )
467 # Strip a blank last line
468 my $last_line = pop(@{$comment->{TEXT}});
469 if (defined($last_line) && $last_line ne "")
472 push (@{$comment->{TEXT}},$last_line);
475 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
477 # Create a 'not implemented' comment
478 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
484 if ($parse_state == 5) # Processing the comment
486 # Process it, if it has any text
487 if (@{$comment->{TEXT}} > 0)
489 if ($extra_comment == 1)
491 process_extra_comment($comment);
495 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
496 process_comment($comment);
499 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
501 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
509 # Standardise a comments text for consistency
510 sub process_comment_text
512 my $comment = shift(@_);
514 for (@{$comment->{TEXT}})
518 # Map I/O values. These come in too many formats to standardise now....
519 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
520 s/\[O\]|\[o\]|\[out\]\[OUT\]/\[Out\]/g;
521 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
522 # TRUE/FALSE/NULL are defines, capitilise them
524 s/False|false/FALSE/g;
526 # Preferred capitalisations
527 s/ wine| WINE/ Wine/g;
528 s/ API | api / Api /g;
530 s/ URL | url / Url /g;
531 s/WIN16|win16/Win16/g;
532 s/WIN32|win32/Win32/g;
533 s/WIN64|win64/Win64/g;
536 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
537 s/ \:/\:/g; # Colons to the left
538 s/ \;/\;/g; # Semi-colons too
540 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
541 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
542 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
544 s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g;
545 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
546 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
547 s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
549 s/( chars)/characters/g;
550 s/( info )/ information /g;
551 s/( app )/ application /g;
552 s/( apps )/ applications /g;
557 # Standardise our comment and output it if it is suitable.
560 my $comment = shift(@_);
562 # Don't process this comment if the function isnt exported
563 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
565 if (!defined($spec_details))
567 if ($opt_verbose > 2)
569 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
570 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
575 if ($comment->{COMMENT_NAME} eq "@")
577 # Create an implementation name
578 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
581 my $exported_names = $spec_details->{EXPORTED_NAMES};
582 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
584 if (!defined($export_index))
586 # Perhaps the comment uses the implementation name?
587 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
588 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
590 if (!defined($export_index))
592 # This function doesn't appear to be exported. hmm.
593 if ($opt_verbose > 2)
595 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
596 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
601 # When the function is exported twice we have the second name below the first
602 # (you see this a lot in ntdll, but also in some other places).
603 my $first_line = ${@{$comment->{TEXT}}}[1];
605 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
607 # Found a second name - mark it as documented
608 my $alt_index = $exported_names->{$1};
609 if (defined($alt_index))
611 if ($opt_verbose > 2)
613 print "Info: Found alternate name '",$1,"\n";
615 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
616 @$alt_export[4] |= 1;
617 $spec_details->{NUM_DOCS}++;
618 ${@{$comment->{TEXT}}}[1] = "";
622 if (@{$spec_details->{CURRENT_EXTRA}})
624 # We have an extra comment that might be related to this one
625 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
626 my $current_name = $current_comment->{COMMENT_NAME};
627 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
629 if ($opt_verbose > 2)
631 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
633 # Add a reference to this comment to our extra comment
634 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
638 # We want our docs generated using the implementation name, so they are unique
639 my $export = @{$spec_details->{EXPORTS}}[$export_index];
640 $comment->{COMMENT_NAME} = @$export[3];
641 $comment->{ALT_NAME} = @$export[2];
643 # Mark the function as documented
644 $spec_details->{NUM_DOCS}++;
647 # This file is used by the DLL - Make sure we get our contributors right
648 push (@{$spec_details->{SOURCES}},$comment->{FILE});
650 # If we have parameter comments in the prototype, extract them
651 my @parameter_comments;
652 for (@{$comment->{PROTOTYPE}})
654 s/ *\, */\,/g; # Strip spaces from around commas
656 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
658 my $parameter_comment = $2;
659 if (!$parameter_comment =~ /^\[/ )
661 # Add [IO] markers so we format the comment correctly
662 $parameter_comment = "[fixme] ".$parameter_comment;
664 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
666 # Add the parameter name
667 $parameter_comment = $2." ".$parameter_comment;
669 push (@parameter_comments, $parameter_comment);
673 # If we extracted any prototype comments, add them to the comment text.
674 if (@parameter_comments)
676 @parameter_comments = ("PARAMS", @parameter_comments);
677 my @new_comment = ();
678 my $inserted_params = 0;
680 for (@{$comment->{TEXT}})
682 if ( $inserted_params == 0 && /^[A-Z]+$/ )
684 # Found a section header, so this is where we insert
685 push (@new_comment, @parameter_comments);
686 $inserted_params = 1;
688 push (@new_comment, $_);
690 if ($inserted_params == 0)
692 # Add them to the end
693 push (@new_comment, @parameter_comments);
695 $comment->{TEXT} = [@new_comment];
698 if ($opt_fussy == 1 && $opt_output_empty == 0)
700 # Reject any comment that doesn't have a description or a RETURNS section.
701 # This is the default for now, 'coz many comments aren't suitable.
702 my $found_returns = 0;
703 my $found_description_text = 0;
704 my $in_description = 0;
705 for (@{$comment->{TEXT}})
712 elsif ( /^DESCRIPTION$/ )
716 elsif ($in_description == 1)
720 if ( /^See ([A-Za-z0-9_]+)\.$/ || /^Unicode version of ([A-Za-z0-9_]+)\.$/)
722 # Dont reject comments that refer to their A/W couterpart
725 $found_description_text = 1;
733 if ($found_returns == 0 || $found_description_text == 0)
735 if ($opt_verbose > 2)
737 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
738 "description and/or RETURNS section, skipping\n";
740 $spec_details->{NUM_DOCS}--;
746 process_comment_text($comment);
748 # Strip the prototypes return value, call convention, name and brackets
749 # (This leaves it as a list of types and names, or empty for void functions)
750 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
751 $prototype =~ s/ / /g;
752 $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
753 $comment->{RETURNS} = $1;
754 $prototype =~ s/ *\).*//; # Strip end bracket
755 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
756 $prototype =~ s/ *\, */\,/g; # Strip space around commas
757 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
758 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Seperate pointers from parameter name
759 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
761 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
765 if ($comment->{COMMENT_NAME} ne "")
767 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
769 my $exit_value = $? >> 8;
770 if ($exit_value == 0)
775 $h_file = `basename $tmp`;
779 elsif ($comment->{ALT_NAME} ne "")
781 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
783 my $exit_value = $? >> 8;
784 if ($exit_value == 0)
789 $h_file = `basename $tmp`;
797 $h_file = "Not defined in a Wine header";
801 $h_file = "Defined in \"".$h_file."\"";
805 my $c_file = $comment->{FILE};
806 if ($opt_wine_root_dir ne "")
808 my $cfile = $pwd."/".$c_file; # Current dir + file
809 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
810 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
811 $cfile =~ s/\n//; # Strip newline
812 my $newfile = $c_file;
813 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
814 $cfile = $cfile."/".$newfile; # Append filename to base path
815 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
816 $cfile =~ s/\/\//\//g; # Remove any double slashes
817 $cfile =~ s/^\/+//; # Strip initial directory slash
820 $c_file = "Implemented in \"".$c_file."\"";
822 # Add the implementation details
823 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
825 my $source_details = $source_files{$comment->{FILE}}[0];
826 if ($source_details->{DEBUG_CHANNEL} ne "")
828 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\"");
831 # Write out the documentation for the API
832 output_comment($comment)
835 # process our extra comment and output it if it is suitable.
836 sub process_extra_comment
838 my $comment = shift(@_);
840 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
842 if (!defined($spec_details))
844 if ($opt_verbose > 2)
846 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
847 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
852 # Check first to see if this is documentation for the DLL.
853 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
855 if ($opt_verbose > 2)
857 print "Info: Found DLL documentation\n";
859 for (@{$comment->{TEXT}})
861 push (@{$spec_details->{DESCRIPTION}}, $_);
866 # Add the comment to the DLL page as a link
867 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
869 # If we have a prototype, process as a regular comment
870 if (@{$comment->{PROTOTYPE}})
872 $comment->{ORDINAL} = "@";
874 # Add an index for the comment name
875 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
877 # Add a fake exported entry
878 $spec_details->{NUM_EXPORTS}++;
879 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
880 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
881 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
882 push (@{$spec_details->{EXPORTS}},[@export]);
883 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
884 process_comment($comment);
888 if ($opt_verbose > 0)
890 print "Processing ",$comment->{COMMENT_NAME},"\n";
893 if (@{$spec_details->{CURRENT_EXTRA}})
895 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
897 if ($opt_verbose > 0)
899 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
901 # Output the current comment
902 process_comment_text($current_comment);
903 output_open_api_file($current_comment->{COMMENT_NAME});
904 output_api_header($current_comment);
905 output_api_name($current_comment);
906 output_api_comment($current_comment);
907 output_api_footer($current_comment);
908 output_close_api_file();
911 if ($opt_verbose > 2)
913 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
918 FILE => $comment->{FILE},
919 COMMENT_NAME => $comment->{COMMENT_NAME},
920 ALT_NAME => $comment->{ALT_NAME},
921 DLL_NAME => $comment->{DLL_NAME},
922 ORDINAL => $comment->{ORDINAL},
923 RETURNS => $comment->{RETURNS},
928 for (@{$comment->{TEXT}})
930 push (@{$comment_copy->{TEXT}}, $_);
932 # Set this comment to be the current extra comment
933 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
936 # Write a standardised comment out in the appropriate format
939 my $comment = shift(@_);
941 if ($opt_verbose > 0)
943 print "Processing ",$comment->{COMMENT_NAME},"\n";
946 if ($opt_verbose > 4)
949 for (@{$comment->{PROTOTYPE}})
954 print "--COMMENT--\n";
955 for (@{$comment->{TEXT} })
961 output_open_api_file($comment->{COMMENT_NAME});
962 output_api_header($comment);
963 output_api_name($comment);
964 output_api_synopsis($comment);
965 output_api_comment($comment);
966 output_api_footer($comment);
967 output_close_api_file();
970 # Write out an index file for each .spec processed
971 sub process_index_files
973 foreach my $spec_file (keys %spec_files)
975 my $spec_details = $spec_files{$spec_file}[0];
976 if (defined ($spec_details->{DLL_NAME}))
978 if (@{$spec_details->{CURRENT_EXTRA}})
980 # We have an unwritten extra comment, write it
981 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
982 process_extra_comment($current_comment);
983 @{$spec_details->{CURRENT_EXTRA}} = ();
985 output_spec($spec_details);
990 # Write a spec files documentation out in the appropriate format
993 my $spec_details = shift(@_);
995 if ($opt_verbose > 2)
997 print "Writing:",$spec_details->{DLL_NAME},"\n";
1000 # Use the comment output functions for consistency
1003 FILE => $spec_details->{DLL_NAME},
1004 COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1005 ALT_NAME => $spec_details->{DLL_NAME},
1012 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1013 $spec_details->{NUM_FUNCS};
1014 my $percent_implemented = 0;
1015 if ($total_implemented)
1017 $percent_implemented = $total_implemented /
1018 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1020 $percent_implemented = int($percent_implemented);
1021 my $percent_documented = 0;
1022 if ($spec_details->{NUM_DOCS})
1024 # Treat forwards and data as documented funcs for statistics
1025 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1026 $percent_documented = int($percent_documented);
1029 # Make a list of the contributors to this DLL. Do this only for the source
1030 # files that make up the DLL, because some directories specify multiple dlls.
1033 for (@{$spec_details->{SOURCES}})
1035 my $source_details = $source_files{$_}[0];
1036 for (@{$source_details->{CONTRIBUTORS}})
1038 push (@contributors, $_);
1043 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1044 @contributors = sort @contributors;
1046 # Remove duplicates and blanks
1047 for(my $i=0; $i<@contributors; $i++)
1049 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1051 $contributors[$i-1] = $contributors[$i];
1055 @contributors = grep(!$saw{$_}++, @contributors);
1057 if ($opt_verbose > 3)
1059 print "Contributors:\n";
1065 my $contribstring = join (", ", @contributors);
1067 # Create the initial comment text
1068 @{$comment->{TEXT}} = (
1070 $comment->{COMMENT_NAME}
1073 # Add the description, if we have one
1074 if (@{$spec_details->{DESCRIPTION}})
1076 push (@{$comment->{TEXT}}, "DESCRIPTION");
1077 for (@{$spec_details->{DESCRIPTION}})
1079 push (@{$comment->{TEXT}}, $_);
1083 # Add the statistics and contributors
1084 push (@{$comment->{TEXT}},
1086 "Forwards: ".$spec_details->{NUM_FORWARDS},
1087 "Variables: ".$spec_details->{NUM_VARS},
1088 "Stubs: ".$spec_details->{NUM_STUBS},
1089 "Functions: ".$spec_details->{NUM_FUNCS},
1090 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1091 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1092 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1094 "The following people hold copyrights on the source files comprising this dll:",
1097 "Note: This list may not be complete.",
1098 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1102 if ($opt_output_format eq "h")
1104 # Add the exports to the comment text
1105 push (@{$comment->{TEXT}},"EXPORTS");
1106 my $exports = $spec_details->{EXPORTS};
1111 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1112 if (@$_[1] eq "forward")
1114 my $forward_dll = @$_[3];
1115 $forward_dll =~ s/\.(.*)//;
1116 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1118 elsif (@$_[1] eq "extern")
1120 $line = @$_[2]." (extern)";
1122 elsif (@$_[1] eq "stub")
1124 $line = @$_[2]." (stub)";
1126 elsif (@$_[1] eq "fake")
1128 # Don't add this function here, it gets listed with the extra documentation
1130 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1132 $line = @$_[2]." (data)";
1140 $line = @$_[2]." (implemented as ".@$_[3]."())";
1141 if (@$_[2] ne @$_[3])
1143 $line = @$_[2]." (implemented as ".@$_[3]."())";
1147 $line = @$_[2]."()";
1152 $line = @$_[2]." (not documented)";
1157 push (@{$comment->{TEXT}}, $line, "");
1161 # Add links to the extra documentation
1162 if (@{$spec_details->{EXTRA_COMMENTS}})
1164 push (@{$comment->{TEXT}}, "SEE ALSO");
1166 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1167 for (@{$spec_details->{EXTRA_COMMENTS}})
1169 push (@{$comment->{TEXT}}, $_."()", "");
1173 # Write out the document
1174 output_open_api_file($spec_details->{DLL_NAME});
1175 output_api_header($comment);
1176 output_api_comment($comment);
1177 output_api_footer($comment);
1178 output_close_api_file();
1180 # Add this dll to the database of dll names
1181 my $output_file = $opt_output_directory."/dlls.db";
1183 # Append the dllname to the output db of names
1184 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1185 print DLLDB $spec_details->{DLL_NAME},"\n";
1188 if ($opt_output_format eq "s")
1190 output_sgml_dll_file($spec_details);
1198 # Only these functions know anything about formatting for a specific
1199 # output type. The functions above work only with plain text.
1200 # This is to allow new types of output to be added easily.
1203 sub output_open_api_file
1205 my $output_name = shift(@_);
1206 $output_name = $opt_output_directory."/".$output_name;
1208 if ($opt_output_format eq "h")
1210 $output_name = $output_name.".html";
1212 elsif ($opt_output_format eq "s")
1214 $output_name = $output_name.".sgml";
1218 $output_name = $output_name.".".$opt_manual_section;
1220 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1223 # Close the api file
1224 sub output_close_api_file
1229 # Output the api file header
1230 sub output_api_header
1232 my $comment = shift(@_);
1234 if ($opt_output_format eq "h")
1236 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1237 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1238 print OUTPUT "<HTML>\n<HEAD>\n";
1239 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1240 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1241 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1242 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1244 elsif ($opt_output_format eq "s")
1246 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1248 "<title>$comment->{COMMENT_NAME}</title>\n";
1252 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1253 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1254 "Wine API\" \"Wine API\"\n";
1258 sub output_api_footer
1260 if ($opt_output_format eq "h")
1262 print OUTPUT "<hr><p><i class=\"copy\">Copyright © ".$year." The Wine Project.".
1263 "Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1264 "Generated $date</i></p>\n</body>\n</html>\n";
1266 elsif ($opt_output_format eq "s")
1268 print OUTPUT "</sect1>\n";
1276 sub output_api_section_start
1278 my $comment = shift(@_);
1279 my $section_name = shift(@_);
1281 if ($opt_output_format eq "h")
1283 print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1285 elsif ($opt_output_format eq "s")
1287 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1291 print OUTPUT "\n\.SH ",$section_name,"\n";
1295 sub output_api_section_end
1297 # Not currently required by any output formats
1302 my $comment = shift(@_);
1304 output_api_section_start($comment,"NAME");
1306 my $dll_ordinal = "";
1307 if ($comment->{ORDINAL} ne "")
1309 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1311 if ($opt_output_format eq "h")
1313 print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1314 "</b> <i class=\"dll_ord\">",
1315 ,$dll_ordinal,"</i></p>\n";
1317 elsif ($opt_output_format eq "s")
1319 print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>",
1320 $dll_ordinal,"</emphasis>\n</para>\n";
1324 print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1327 output_api_section_end();
1330 sub output_api_synopsis
1332 my $comment = shift(@_);
1335 output_api_section_start($comment,"SYNOPSIS");
1337 if ($opt_output_format eq "h")
1339 print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1340 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1342 elsif ($opt_output_format eq "s")
1344 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1345 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1349 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1350 @fmt = ("", "\n", "\\fI", "\\fR");
1353 # Since our prototype is output in a pre-formatted block, line up the
1354 # parameters and parameter comments in the same column.
1356 # First caluculate where the columns should start
1357 my $biggest_length = 0;
1358 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1360 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1361 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1363 my $length = length $1;
1364 if ($length > $biggest_length)
1366 $biggest_length = $length;
1371 # Now pad the string with blanks
1372 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1374 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1375 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1377 my $pad_len = $biggest_length - length $1;
1378 my $padding = " " x ($pad_len);
1379 ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1383 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1385 # Format the parameter name
1386 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1387 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1388 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1392 if ($opt_output_format eq "h")
1394 print OUTPUT " )\n\n</pre></p>\n";
1396 elsif ($opt_output_format eq "s")
1398 print OUTPUT " )\n</screen>\n";
1402 print OUTPUT " )\n";
1405 output_api_section_end();
1408 sub output_api_comment
1410 my $comment = shift(@_);
1411 my $open_paragraph = 0;
1416 if ($opt_output_format eq "h")
1418 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1419 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1420 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1421 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1422 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1424 elsif ($opt_output_format eq "s")
1426 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1427 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1428 "<screen>\n","</screen>\n",
1429 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1430 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1431 "</entry>","</entry><entry>");
1435 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1436 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1439 # Extract the parameter names
1440 my @parameter_names;
1441 for (@{$comment->{PROTOTYPE}})
1443 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1445 push (@parameter_names, $2);
1449 for (@{$comment->{TEXT}})
1451 if ($opt_output_format eq "h" || $opt_output_format eq "s")
1453 # Map special characters
1457 s/\([Cc]\)/\©/g;
1466 if ($open_paragraph == 1)
1468 # Close the open paragraph
1469 print OUTPUT $fmt[1];
1470 $open_paragraph = 0;
1473 print OUTPUT $fmt[12];
1476 if ($opt_output_format eq "")
1478 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1480 print OUTPUT $_,"\n";
1485 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1486 # Highlight literal chars
1487 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1488 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1489 # Highlight numeric constants
1490 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1492 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1493 # FIXME: Using bullet points for leading '-' would look nicer.
1494 if ($open_paragraph == 1)
1496 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1497 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1501 s/^(\-)/$fmt[4]$1$fmt[5]/;
1502 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1505 if ($opt_output_format eq "h")
1507 # Html uses links for API calls
1508 s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1509 # And references to COM objects (hey, they'll get documented one day)
1510 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1511 # Convert any web addresses to real links
1512 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1516 if ($opt_output_format eq "")
1518 # Give the man section for API calls
1519 s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1523 # Highlight API calls
1524 s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1527 # And references to COM objects
1528 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1533 # Finish the raw output
1534 print OUTPUT $fmt[13];
1538 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1540 # Start of a new section
1541 if ($open_paragraph == 1)
1543 if ($param_docs == 1)
1545 print OUTPUT $fmt[17],$fmt[15];
1549 print OUTPUT $fmt[1];
1551 $open_paragraph = 0;
1553 output_api_section_start($comment,$_);
1556 print OUTPUT $fmt[14];
1561 #print OUTPUT $fmt[15];
1567 # Empty line, indicating a new paragraph
1568 if ($open_paragraph == 1)
1570 if ($param_docs == 0)
1572 print OUTPUT $fmt[1];
1573 $open_paragraph = 0;
1579 if ($param_docs == 1)
1581 if ($open_paragraph == 1)
1583 # For parameter docs, put each parameter into a new paragraph/table row
1584 print OUTPUT $fmt[17];
1585 $open_paragraph = 0;
1587 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19]/; # Format In/Out
1591 # Within paragraph lines, prevent lines running together
1595 # Format parameter names where they appear in the comment
1596 for my $parameter_name (@parameter_names)
1598 s/(^|[ \.\,\(\-])($parameter_name)($|[ \.\)\,\-])/$1$fmt[8]$2$fmt[9]$3/g;
1601 if ($open_paragraph == 0)
1603 if ($param_docs == 1)
1605 print OUTPUT $fmt[16];
1609 print OUTPUT $fmt[0];
1611 $open_paragraph = 1;
1613 # Anything in all uppercase on its own gets emphasised
1614 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1622 print OUTPUT $fmt[13];
1624 if ($open_paragraph == 1)
1626 print OUTPUT $fmt[1];
1630 # Create the master index file
1631 sub output_master_index_files
1633 if ($opt_output_format eq "")
1635 return; # No master index for man pages
1638 # Use the comment output functions for consistency
1642 COMMENT_NAME => "The Wine Api Guide",
1643 ALT_NAME => "The Wine Api Guide",
1651 if ($opt_output_format eq "s")
1653 $comment->{COMMENT_NAME} = "Introduction";
1654 $comment->{ALT_NAME} = "Introduction",
1656 elsif ($opt_output_format eq "h")
1658 @{$comment->{TEXT}} = (
1660 $comment->{COMMENT_NAME},
1665 # Create the initial comment text
1666 push (@{$comment->{TEXT}},
1667 "This document describes the Api calls made available",
1668 "by Wine. They are grouped by the dll that exports them.",
1670 "Please do not edit this document, since it is generated automatically",
1671 "from the Wine source code tree. Details on updating this documentation",
1672 "are given in the \"Wine Developers Guide\".",
1674 "Api documentation is generally written by the person who ",
1675 "implements a given Api call. Authors of each dll are listed in the overview ",
1676 "section for that dll. Additional contributors who have updated source files ",
1677 "but have not entered their names in a copyright statement are noted by an ",
1678 "entry in the file \"Changelog\" from the Wine source code distribution.",
1682 # Read in all dlls from the database of dll names
1683 my $input_file = $opt_output_directory."/dlls.db";
1684 my @dlls = `cat $input_file|sort|uniq`;
1686 if ($opt_output_format eq "h")
1688 # HTML gets a list of all the dlls. For docbook the index creates this for us
1689 push (@{$comment->{TEXT}},
1691 "The following dlls are provided by Wine:",
1694 # Add the dlls to the comment
1697 $_ =~ s/(\..*)?\n/\(\)/;
1698 push (@{$comment->{TEXT}}, $_, "");
1700 output_open_api_file("index");
1702 elsif ($opt_output_format eq "s")
1704 # Just write this as the initial blurb, with a chapter heading
1705 output_open_api_file("blurb");
1706 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1709 # Write out the document
1710 output_api_header($comment);
1711 output_api_comment($comment);
1712 output_api_footer($comment);
1713 if ($opt_output_format eq "s")
1715 print OUTPUT "</chapter>\n" # finish the chapter
1717 output_close_api_file();
1719 if ($opt_output_format eq "s")
1721 output_sgml_master_file(\@dlls);
1724 if ($opt_output_format eq "h")
1726 output_html_stylesheet();
1727 # FIXME: Create an alphabetical index
1732 # Write the master wine-api.sgml, linking it to each dll.
1733 sub output_sgml_master_file
1735 my $dlls = shift(@_);
1737 output_open_api_file("wine-api");
1738 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1739 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1740 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1745 $_ =~ s/(\..*)?\n//;
1746 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1749 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1750 print OUTPUT " &blurb;\n";
1754 print OUTPUT " &",$_,";\n"
1756 print OUTPUT "\n\n</book>\n";
1758 output_close_api_file();
1761 # Produce the sgml for the dll chapter from the generated files
1762 sub output_sgml_dll_file
1764 my $spec_details = shift(@_);
1766 # Make a list of all the documentation files to include
1767 my $exports = $spec_details->{EXPORTS};
1768 my @source_files = ();
1771 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1772 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1773 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1775 # A documented function
1776 push (@source_files,@$_[3]);
1780 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1782 @source_files = sort @source_files;
1784 # create a new chapter for this dll
1785 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1786 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1787 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1788 output_close_api_file();
1790 # Add the sorted documentation, cleaning up as we go
1791 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1794 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1795 `rm -f $opt_output_directory/$_.sgml`;
1798 # close the chapter, and overwite the dll source
1799 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1800 print OUTPUT "</chapter>\n";
1802 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1805 # Output the stylesheet for HTML output
1806 sub output_html_stylesheet
1808 if ($opt_output_format ne "h")
1814 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
1816 * Default styles for Wine HTML Documentation.
1818 * This style sheet should be altered to suit your needs/taste.
1820 BODY { /* Page body */
1821 background-color: white;
1823 font-family: Tahomsans-serif;
1827 a:link { color: #4444ff; } /* Links */
1828 a:visited { color: #333377 }
1829 a:active { color: #0000dd }
1830 H2.section { /* Section Headers */
1831 font-family: sans-serif;
1833 background-color: #F0F0FE;
1835 margin-right: 1.0in;
1837 b.func_name { /* Function Name */
1841 i.dll_ord { /* Italicised DLL+ordinal */
1843 font-family: sans-serif;
1846 p { /* Paragraphs */
1848 margin-right: 0.5in;
1850 table { /* tables */
1852 margin-right: 0.5in;
1854 pre.proto /* API Function prototype */
1856 border-style: solid;
1858 border-color: #777777;
1859 background-color: #F0F0BB;
1862 vertical-align: top;
1864 margin-right: 1.0in;
1866 tt.param { /* Parameter name */
1870 tt.const { /* Constant */
1873 i.in_out { /* In/Out */
1877 tt.coderef { /* Code in description text */
1880 b.emp /* Emphasis */ {
1884 i.footer { /* Footer */
1885 font-family: sans-serif;
1891 my $output_file = "$opt_output_directory/apidoc.css";
1892 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";