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 if ($implementation_name eq "")
254 $implementation_name = $exported_name;
256 # Add indices for the exported and implementation names
257 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
258 if ($implementation_name ne $exported_name)
260 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
263 # Add the exported entry
264 $spec_details->{NUM_EXPORTS}++;
266 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
267 push (@{$spec_details->{EXPORTS}},[@export]);
272 # Add this .spec files details to the list of .spec files
273 $spec_files{$uc_dll_name} = [$spec_details];
276 # Read each source file, extract comments, and generate API documentation if appropriate
277 sub process_source_file
279 my $source_file = shift(@_);
287 FILE => $source_file,
297 my $ignore_blank_lines = 1;
298 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
300 if ($opt_verbose > 0)
302 print "Processing ".$source_file."\n";
304 open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n";
306 # Add this source file to the list of source files
307 $source_files{$source_file} = [$source_details];
311 s/\n$//; # Strip newline
312 s/^\s+//; # Strip leading space
313 s/\s+$//; # Strip trailing space
316 # Strip multiple tabs & spaces to a single space
320 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
322 # Extract a contributor to this file
323 my $contributor = $2;
324 $contributor =~ s/ *$//;
325 $contributor =~ s/^by //;
326 $contributor =~ s/\.$//;
327 $contributor =~ s/ (for .*)/ \($1\)/;
328 if ($contributor ne "")
330 if ($opt_verbose > 3)
332 print "Info: Found contributor:'".$contributor."'\n";
334 push (@{$source_details->{CONTRIBUTORS}},$contributor);
337 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
339 # Extract the debug channel to use
340 if ($opt_verbose > 3)
342 print "Info: Found debug channel:'".$1."'\n";
344 $source_details->{DEBUG_CHANNEL} = $1;
347 if ($parse_state == 0) # Searching for a comment
351 # Found a comment start
352 $comment->{COMMENT_NAME} = "";
353 $comment->{ALT_NAME} = "";
354 $comment->{DLL_NAME} = "";
355 $comment->{ORDINAL} = "";
356 $comment->{RETURNS} = "";
357 $comment->{PROTOTYPE} = [];
358 $comment->{TEXT} = [];
359 $ignore_blank_lines = 1;
364 elsif ($parse_state == 1) # Reading in a comment
368 # Found the end of the comment
371 elsif ( s/^\*\|/\|/ )
373 # A line of comment not meant to be pre-processed
374 push (@{$comment->{TEXT}},$_); # Add the comment text
376 elsif ( s/^ *\** *// )
378 # A line of comment, starting with an asterisk
379 if ( /^[A-Z]+$/ || $_ eq "")
381 # This is a section start, so skip blank lines before and after it.
382 my $last_line = pop(@{$comment->{TEXT}});
383 if (defined($last_line) && $last_line ne "")
386 push (@{$comment->{TEXT}},$last_line);
390 $ignore_blank_lines = 1;
394 $ignore_blank_lines = 0;
398 if ($ignore_blank_lines == 0 || $_ ne "")
400 push (@{$comment->{TEXT}},$_); # Add the comment text
405 # This isn't a well formatted comment: look for the next one
409 elsif ($parse_state == 2) # Finished reading in a comment
411 if ( /(WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
413 # Comment is followed by a function definition
414 $parse_state = 4; # Fall through to read prototype
418 # Allow cpp directives and blank lines between the comment and prototype
419 if ($extra_comment == 1)
421 # An extra comment not followed by a function definition
422 $parse_state = 5; # Fall through to process comment
424 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
426 # This isn't a well formatted comment: look for the next one
427 if ($opt_verbose > 1)
429 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
435 elsif ($parse_state == 3) # Reading in the first line of a comment
438 if ( /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
440 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
441 $comment->{COMMENT_NAME} = $1;
442 $comment->{DLL_NAME} = uc $3;
443 $comment->{ORDINAL} = $4;
444 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
447 elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ )
449 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
450 $comment->{COMMENT_NAME} = $1;
451 $comment->{DLL_NAME} = uc $2;
452 $comment->{ORDINAL} = "";
458 # This isn't a well formatted comment: look for the next one
463 if ($parse_state == 4) # Reading in the function definition
465 push (@{$comment->{PROTOTYPE}},$_);
466 # Strip comments from the line before checking for ')'
467 my $stripped_line = $_;
468 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
469 if ( $stripped_line =~ /\)/ )
471 # Strip a blank last line
472 my $last_line = pop(@{$comment->{TEXT}});
473 if (defined($last_line) && $last_line ne "")
476 push (@{$comment->{TEXT}},$last_line);
479 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
481 # Create a 'not implemented' comment
482 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
488 if ($parse_state == 5) # Processing the comment
490 # Process it, if it has any text
491 if (@{$comment->{TEXT}} > 0)
493 if ($extra_comment == 1)
495 process_extra_comment($comment);
499 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
500 process_comment($comment);
503 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
505 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
513 # Standardise a comments text for consistency
514 sub process_comment_text
516 my $comment = shift(@_);
518 for (@{$comment->{TEXT}})
522 # Map I/O values. These come in too many formats to standardise now....
523 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
524 s/\[O\]|\[o\]|\[out\]\[OUT\]/\[Out\]/g;
525 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
526 # TRUE/FALSE/NULL are defines, capitilise them
528 s/False|false/FALSE/g;
530 # Preferred capitalisations
531 s/ wine| WINE/ Wine/g;
532 s/ API | api / Api /g;
534 s/ URL | url / Url /g;
535 s/WIN16|win16/Win16/g;
536 s/WIN32|win32/Win32/g;
537 s/WIN64|win64/Win64/g;
540 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
541 s/ \:/\:/g; # Colons to the left
542 s/ \;/\;/g; # Semi-colons too
544 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
545 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
546 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
548 s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g;
549 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
550 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
551 s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
553 s/( chars)/characters/g;
554 s/( info )/ information /g;
555 s/( app )/ application /g;
556 s/( apps )/ applications /g;
561 # Standardise our comment and output it if it is suitable.
564 my $comment = shift(@_);
566 # Don't process this comment if the function isnt exported
567 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
569 if (!defined($spec_details))
571 if ($opt_verbose > 2)
573 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
574 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
579 if ($comment->{COMMENT_NAME} eq "@")
581 # Create an implementation name
582 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
585 my $exported_names = $spec_details->{EXPORTED_NAMES};
586 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
588 if (!defined($export_index))
590 # Perhaps the comment uses the implementation name?
591 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
592 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
594 if (!defined($export_index))
596 # This function doesn't appear to be exported. hmm.
597 if ($opt_verbose > 2)
599 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
600 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
605 # When the function is exported twice we have the second name below the first
606 # (you see this a lot in ntdll, but also in some other places).
607 my $first_line = ${@{$comment->{TEXT}}}[1];
609 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
611 # Found a second name - mark it as documented
612 my $alt_index = $exported_names->{$1};
613 if (defined($alt_index))
615 if ($opt_verbose > 2)
617 print "Info: Found alternate name '",$1,"\n";
619 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
620 @$alt_export[4] |= 1;
621 $spec_details->{NUM_DOCS}++;
622 ${@{$comment->{TEXT}}}[1] = "";
626 if (@{$spec_details->{CURRENT_EXTRA}})
628 # We have an extra comment that might be related to this one
629 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
630 my $current_name = $current_comment->{COMMENT_NAME};
631 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
633 if ($opt_verbose > 2)
635 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
637 # Add a reference to this comment to our extra comment
638 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
642 # We want our docs generated using the implementation name, so they are unique
643 my $export = @{$spec_details->{EXPORTS}}[$export_index];
644 $comment->{COMMENT_NAME} = @$export[3];
645 $comment->{ALT_NAME} = @$export[2];
647 # Mark the function as documented
648 $spec_details->{NUM_DOCS}++;
651 # This file is used by the DLL - Make sure we get our contributors right
652 push (@{$spec_details->{SOURCES}},$comment->{FILE});
654 # If we have parameter comments in the prototype, extract them
655 my @parameter_comments;
656 for (@{$comment->{PROTOTYPE}})
658 s/ *\, */\,/g; # Strip spaces from around commas
660 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
662 my $parameter_comment = $2;
663 if (!$parameter_comment =~ /^\[/ )
665 # Add [IO] markers so we format the comment correctly
666 $parameter_comment = "[fixme] ".$parameter_comment;
668 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
670 # Add the parameter name
671 $parameter_comment = $2." ".$parameter_comment;
673 push (@parameter_comments, $parameter_comment);
677 # If we extracted any prototype comments, add them to the comment text.
678 if (@parameter_comments)
680 @parameter_comments = ("PARAMS", @parameter_comments);
681 my @new_comment = ();
682 my $inserted_params = 0;
684 for (@{$comment->{TEXT}})
686 if ( $inserted_params == 0 && /^[A-Z]+$/ )
688 # Found a section header, so this is where we insert
689 push (@new_comment, @parameter_comments);
690 $inserted_params = 1;
692 push (@new_comment, $_);
694 if ($inserted_params == 0)
696 # Add them to the end
697 push (@new_comment, @parameter_comments);
699 $comment->{TEXT} = [@new_comment];
702 if ($opt_fussy == 1 && $opt_output_empty == 0)
704 # Reject any comment that doesn't have a description or a RETURNS section.
705 # This is the default for now, 'coz many comments aren't suitable.
706 my $found_returns = 0;
707 my $found_description_text = 0;
708 my $in_description = 0;
709 for (@{$comment->{TEXT}})
716 elsif ( /^DESCRIPTION$/ )
720 elsif ($in_description == 1)
724 if ( /^See ([A-Za-z0-9_]+)\.$/ || /^Unicode version of ([A-Za-z0-9_]+)\.$/)
726 # Dont reject comments that refer to their A/W couterpart
729 $found_description_text = 1;
737 if ($found_returns == 0 || $found_description_text == 0)
739 if ($opt_verbose > 2)
741 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
742 "description and/or RETURNS section, skipping\n";
744 $spec_details->{NUM_DOCS}--;
750 process_comment_text($comment);
752 # Strip the prototypes return value, call convention, name and brackets
753 # (This leaves it as a list of types and names, or empty for void functions)
754 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
755 $prototype =~ s/ / /g;
756 $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
757 $comment->{RETURNS} = $1;
758 $prototype =~ s/ *\).*//; # Strip end bracket
759 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
760 $prototype =~ s/ *\, */\,/g; # Strip space around commas
761 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
762 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Seperate pointers from parameter name
763 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
765 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
769 if ($comment->{COMMENT_NAME} ne "")
771 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
773 my $exit_value = $? >> 8;
774 if ($exit_value == 0)
779 $h_file = `basename $tmp`;
783 elsif ($comment->{ALT_NAME} ne "")
785 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
787 my $exit_value = $? >> 8;
788 if ($exit_value == 0)
793 $h_file = `basename $tmp`;
801 $h_file = "Not defined in a Wine header";
805 $h_file = "Defined in \"".$h_file."\"";
809 my $c_file = $comment->{FILE};
810 if ($opt_wine_root_dir ne "")
812 my $cfile = $pwd."/".$c_file; # Current dir + file
813 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
814 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
815 $cfile =~ s/\n//; # Strip newline
816 my $newfile = $c_file;
817 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
818 $cfile = $cfile."/".$newfile; # Append filename to base path
819 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
820 $cfile =~ s/\/\//\//g; # Remove any double slashes
821 $cfile =~ s/^\/+//; # Strip initial directory slash
824 $c_file = "Implemented in \"".$c_file."\"";
826 # Add the implementation details
827 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
829 my $source_details = $source_files{$comment->{FILE}}[0];
830 if ($source_details->{DEBUG_CHANNEL} ne "")
832 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\"");
835 # Write out the documentation for the API
836 output_comment($comment)
839 # process our extra comment and output it if it is suitable.
840 sub process_extra_comment
842 my $comment = shift(@_);
844 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
846 if (!defined($spec_details))
848 if ($opt_verbose > 2)
850 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
851 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
856 # Check first to see if this is documentation for the DLL.
857 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
859 if ($opt_verbose > 2)
861 print "Info: Found DLL documentation\n";
863 for (@{$comment->{TEXT}})
865 push (@{$spec_details->{DESCRIPTION}}, $_);
870 # Add the comment to the DLL page as a link
871 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
873 # If we have a prototype, process as a regular comment
874 if (@{$comment->{PROTOTYPE}})
876 $comment->{ORDINAL} = "@";
878 # Add an index for the comment name
879 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
881 # Add a fake exported entry
882 $spec_details->{NUM_EXPORTS}++;
883 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
884 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
885 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
886 push (@{$spec_details->{EXPORTS}},[@export]);
887 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
888 process_comment($comment);
892 if ($opt_verbose > 0)
894 print "Processing ",$comment->{COMMENT_NAME},"\n";
897 if (@{$spec_details->{CURRENT_EXTRA}})
899 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
901 if ($opt_verbose > 0)
903 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
905 # Output the current comment
906 process_comment_text($current_comment);
907 output_open_api_file($current_comment->{COMMENT_NAME});
908 output_api_header($current_comment);
909 output_api_name($current_comment);
910 output_api_comment($current_comment);
911 output_api_footer($current_comment);
912 output_close_api_file();
915 if ($opt_verbose > 2)
917 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
922 FILE => $comment->{FILE},
923 COMMENT_NAME => $comment->{COMMENT_NAME},
924 ALT_NAME => $comment->{ALT_NAME},
925 DLL_NAME => $comment->{DLL_NAME},
926 ORDINAL => $comment->{ORDINAL},
927 RETURNS => $comment->{RETURNS},
932 for (@{$comment->{TEXT}})
934 push (@{$comment_copy->{TEXT}}, $_);
936 # Set this comment to be the current extra comment
937 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
940 # Write a standardised comment out in the appropriate format
943 my $comment = shift(@_);
945 if ($opt_verbose > 0)
947 print "Processing ",$comment->{COMMENT_NAME},"\n";
950 if ($opt_verbose > 4)
953 for (@{$comment->{PROTOTYPE}})
958 print "--COMMENT--\n";
959 for (@{$comment->{TEXT} })
965 output_open_api_file($comment->{COMMENT_NAME});
966 output_api_header($comment);
967 output_api_name($comment);
968 output_api_synopsis($comment);
969 output_api_comment($comment);
970 output_api_footer($comment);
971 output_close_api_file();
974 # Write out an index file for each .spec processed
975 sub process_index_files
977 foreach my $spec_file (keys %spec_files)
979 my $spec_details = $spec_files{$spec_file}[0];
980 if (defined ($spec_details->{DLL_NAME}))
982 if (@{$spec_details->{CURRENT_EXTRA}})
984 # We have an unwritten extra comment, write it
985 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
986 process_extra_comment($current_comment);
987 @{$spec_details->{CURRENT_EXTRA}} = ();
989 output_spec($spec_details);
994 # Write a spec files documentation out in the appropriate format
997 my $spec_details = shift(@_);
999 if ($opt_verbose > 2)
1001 print "Writing:",$spec_details->{DLL_NAME},"\n";
1004 # Use the comment output functions for consistency
1007 FILE => $spec_details->{DLL_NAME},
1008 COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1009 ALT_NAME => $spec_details->{DLL_NAME},
1016 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1017 $spec_details->{NUM_FUNCS};
1018 my $percent_implemented = 0;
1019 if ($total_implemented)
1021 $percent_implemented = $total_implemented /
1022 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1024 $percent_implemented = int($percent_implemented);
1025 my $percent_documented = 0;
1026 if ($spec_details->{NUM_DOCS})
1028 # Treat forwards and data as documented funcs for statistics
1029 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1030 $percent_documented = int($percent_documented);
1033 # Make a list of the contributors to this DLL. Do this only for the source
1034 # files that make up the DLL, because some directories specify multiple dlls.
1037 for (@{$spec_details->{SOURCES}})
1039 my $source_details = $source_files{$_}[0];
1040 for (@{$source_details->{CONTRIBUTORS}})
1042 push (@contributors, $_);
1047 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1048 @contributors = sort @contributors;
1050 # Remove duplicates and blanks
1051 for(my $i=0; $i<@contributors; $i++)
1053 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1055 $contributors[$i-1] = $contributors[$i];
1059 @contributors = grep(!$saw{$_}++, @contributors);
1061 if ($opt_verbose > 3)
1063 print "Contributors:\n";
1069 my $contribstring = join (", ", @contributors);
1071 # Create the initial comment text
1072 @{$comment->{TEXT}} = (
1074 $comment->{COMMENT_NAME}
1077 # Add the description, if we have one
1078 if (@{$spec_details->{DESCRIPTION}})
1080 push (@{$comment->{TEXT}}, "DESCRIPTION");
1081 for (@{$spec_details->{DESCRIPTION}})
1083 push (@{$comment->{TEXT}}, $_);
1087 # Add the statistics and contributors
1088 push (@{$comment->{TEXT}},
1090 "Forwards: ".$spec_details->{NUM_FORWARDS},
1091 "Variables: ".$spec_details->{NUM_VARS},
1092 "Stubs: ".$spec_details->{NUM_STUBS},
1093 "Functions: ".$spec_details->{NUM_FUNCS},
1094 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1095 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1096 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1098 "The following people hold copyrights on the source files comprising this dll:",
1101 "Note: This list may not be complete.",
1102 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1106 if ($opt_output_format eq "h")
1108 # Add the exports to the comment text
1109 push (@{$comment->{TEXT}},"EXPORTS");
1110 my $exports = $spec_details->{EXPORTS};
1115 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1116 if (@$_[1] eq "forward")
1118 my $forward_dll = @$_[3];
1119 $forward_dll =~ s/\.(.*)//;
1120 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1122 elsif (@$_[1] eq "extern")
1124 $line = @$_[2]." (extern)";
1126 elsif (@$_[1] eq "stub")
1128 $line = @$_[2]." (stub)";
1130 elsif (@$_[1] eq "fake")
1132 # Don't add this function here, it gets listed with the extra documentation
1134 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1136 $line = @$_[2]." (data)";
1144 $line = @$_[2]." (implemented as ".@$_[3]."())";
1145 if (@$_[2] ne @$_[3])
1147 $line = @$_[2]." (implemented as ".@$_[3]."())";
1151 $line = @$_[2]."()";
1156 $line = @$_[2]." (not documented)";
1161 push (@{$comment->{TEXT}}, $line, "");
1165 # Add links to the extra documentation
1166 if (@{$spec_details->{EXTRA_COMMENTS}})
1168 push (@{$comment->{TEXT}}, "SEE ALSO");
1170 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1171 for (@{$spec_details->{EXTRA_COMMENTS}})
1173 push (@{$comment->{TEXT}}, $_."()", "");
1177 # Write out the document
1178 output_open_api_file($spec_details->{DLL_NAME});
1179 output_api_header($comment);
1180 output_api_comment($comment);
1181 output_api_footer($comment);
1182 output_close_api_file();
1184 # Add this dll to the database of dll names
1185 my $output_file = $opt_output_directory."/dlls.db";
1187 # Append the dllname to the output db of names
1188 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1189 print DLLDB $spec_details->{DLL_NAME},"\n";
1192 if ($opt_output_format eq "s")
1194 output_sgml_dll_file($spec_details);
1202 # Only these functions know anything about formatting for a specific
1203 # output type. The functions above work only with plain text.
1204 # This is to allow new types of output to be added easily.
1207 sub output_open_api_file
1209 my $output_name = shift(@_);
1210 $output_name = $opt_output_directory."/".$output_name;
1212 if ($opt_output_format eq "h")
1214 $output_name = $output_name.".html";
1216 elsif ($opt_output_format eq "s")
1218 $output_name = $output_name.".sgml";
1222 $output_name = $output_name.".".$opt_manual_section;
1224 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1227 # Close the api file
1228 sub output_close_api_file
1233 # Output the api file header
1234 sub output_api_header
1236 my $comment = shift(@_);
1238 if ($opt_output_format eq "h")
1240 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1241 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1242 print OUTPUT "<HTML>\n<HEAD>\n";
1243 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1244 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1245 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1246 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1248 elsif ($opt_output_format eq "s")
1250 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1252 "<title>$comment->{COMMENT_NAME}</title>\n";
1256 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1257 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1258 "Wine API\" \"Wine API\"\n";
1262 sub output_api_footer
1264 if ($opt_output_format eq "h")
1266 print OUTPUT "<hr><p><i class=\"copy\">Copyright © ".$year." The Wine Project.".
1267 "Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1268 "Generated $date</i></p>\n</body>\n</html>\n";
1270 elsif ($opt_output_format eq "s")
1272 print OUTPUT "</sect1>\n";
1280 sub output_api_section_start
1282 my $comment = shift(@_);
1283 my $section_name = shift(@_);
1285 if ($opt_output_format eq "h")
1287 print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1289 elsif ($opt_output_format eq "s")
1291 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1295 print OUTPUT "\n\.SH ",$section_name,"\n";
1299 sub output_api_section_end
1301 # Not currently required by any output formats
1306 my $comment = shift(@_);
1308 output_api_section_start($comment,"NAME");
1310 my $dll_ordinal = "";
1311 if ($comment->{ORDINAL} ne "")
1313 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1315 if ($opt_output_format eq "h")
1317 print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1318 "</b> <i class=\"dll_ord\">",
1319 ,$dll_ordinal,"</i></p>\n";
1321 elsif ($opt_output_format eq "s")
1323 print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>",
1324 $dll_ordinal,"</emphasis>\n</para>\n";
1328 print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1331 output_api_section_end();
1334 sub output_api_synopsis
1336 my $comment = shift(@_);
1339 output_api_section_start($comment,"SYNOPSIS");
1341 if ($opt_output_format eq "h")
1343 print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1344 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1346 elsif ($opt_output_format eq "s")
1348 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1349 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1353 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1354 @fmt = ("", "\n", "\\fI", "\\fR");
1357 # Since our prototype is output in a pre-formatted block, line up the
1358 # parameters and parameter comments in the same column.
1360 # First caluculate where the columns should start
1361 my $biggest_length = 0;
1362 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1364 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1365 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1367 my $length = length $1;
1368 if ($length > $biggest_length)
1370 $biggest_length = $length;
1375 # Now pad the string with blanks
1376 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1378 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1379 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1381 my $pad_len = $biggest_length - length $1;
1382 my $padding = " " x ($pad_len);
1383 ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1387 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1389 # Format the parameter name
1390 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1391 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1392 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1396 if ($opt_output_format eq "h")
1398 print OUTPUT " )\n\n</pre></p>\n";
1400 elsif ($opt_output_format eq "s")
1402 print OUTPUT " )\n</screen>\n";
1406 print OUTPUT " )\n";
1409 output_api_section_end();
1412 sub output_api_comment
1414 my $comment = shift(@_);
1415 my $open_paragraph = 0;
1420 if ($opt_output_format eq "h")
1422 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1423 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1424 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1425 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1426 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1428 elsif ($opt_output_format eq "s")
1430 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1431 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1432 "<screen>\n","</screen>\n",
1433 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1434 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1435 "</entry>","</entry><entry>");
1439 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1440 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1443 # Extract the parameter names
1444 my @parameter_names;
1445 for (@{$comment->{PROTOTYPE}})
1447 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1449 push (@parameter_names, $2);
1453 for (@{$comment->{TEXT}})
1455 if ($opt_output_format eq "h" || $opt_output_format eq "s")
1457 # Map special characters
1461 s/\([Cc]\)/\©/g;
1470 if ($open_paragraph == 1)
1472 # Close the open paragraph
1473 print OUTPUT $fmt[1];
1474 $open_paragraph = 0;
1477 print OUTPUT $fmt[12];
1480 if ($opt_output_format eq "")
1482 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1484 print OUTPUT $_,"\n";
1489 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1490 # Highlight literal chars
1491 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1492 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1493 # Highlight numeric constants
1494 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1496 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1497 # FIXME: Using bullet points for leading '-' would look nicer.
1498 if ($open_paragraph == 1)
1500 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1501 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1505 s/^(\-)/$fmt[4]$1$fmt[5]/;
1506 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1509 if ($opt_output_format eq "h")
1511 # Html uses links for API calls
1512 s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1513 # And references to COM objects (hey, they'll get documented one day)
1514 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1515 # Convert any web addresses to real links
1516 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1520 if ($opt_output_format eq "")
1522 # Give the man section for API calls
1523 s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1527 # Highlight API calls
1528 s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1531 # And references to COM objects
1532 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1537 # Finish the raw output
1538 print OUTPUT $fmt[13];
1542 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1544 # Start of a new section
1545 if ($open_paragraph == 1)
1547 if ($param_docs == 1)
1549 print OUTPUT $fmt[17],$fmt[15];
1553 print OUTPUT $fmt[1];
1555 $open_paragraph = 0;
1557 output_api_section_start($comment,$_);
1560 print OUTPUT $fmt[14];
1565 #print OUTPUT $fmt[15];
1571 # Empty line, indicating a new paragraph
1572 if ($open_paragraph == 1)
1574 if ($param_docs == 0)
1576 print OUTPUT $fmt[1];
1577 $open_paragraph = 0;
1583 if ($param_docs == 1)
1585 if ($open_paragraph == 1)
1587 # For parameter docs, put each parameter into a new paragraph/table row
1588 print OUTPUT $fmt[17];
1589 $open_paragraph = 0;
1591 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19]/; # Format In/Out
1595 # Within paragraph lines, prevent lines running together
1599 # Format parameter names where they appear in the comment
1600 for my $parameter_name (@parameter_names)
1602 s/(^|[ \.\,\(\-])($parameter_name)($|[ \.\)\,\-])/$1$fmt[8]$2$fmt[9]$3/g;
1605 if ($open_paragraph == 0)
1607 if ($param_docs == 1)
1609 print OUTPUT $fmt[16];
1613 print OUTPUT $fmt[0];
1615 $open_paragraph = 1;
1617 # Anything in all uppercase on its own gets emphasised
1618 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1626 print OUTPUT $fmt[13];
1628 if ($open_paragraph == 1)
1630 print OUTPUT $fmt[1];
1634 # Create the master index file
1635 sub output_master_index_files
1637 if ($opt_output_format eq "")
1639 return; # No master index for man pages
1642 # Use the comment output functions for consistency
1646 COMMENT_NAME => "The Wine Api Guide",
1647 ALT_NAME => "The Wine Api Guide",
1655 if ($opt_output_format eq "s")
1657 $comment->{COMMENT_NAME} = "Introduction";
1658 $comment->{ALT_NAME} = "Introduction",
1660 elsif ($opt_output_format eq "h")
1662 @{$comment->{TEXT}} = (
1664 $comment->{COMMENT_NAME},
1669 # Create the initial comment text
1670 push (@{$comment->{TEXT}},
1671 "This document describes the Api calls made available",
1672 "by Wine. They are grouped by the dll that exports them.",
1674 "Please do not edit this document, since it is generated automatically",
1675 "from the Wine source code tree. Details on updating this documentation",
1676 "are given in the \"Wine Developers Guide\".",
1678 "Api documentation is generally written by the person who ",
1679 "implements a given Api call. Authors of each dll are listed in the overview ",
1680 "section for that dll. Additional contributors who have updated source files ",
1681 "but have not entered their names in a copyright statement are noted by an ",
1682 "entry in the file \"Changelog\" from the Wine source code distribution.",
1686 # Read in all dlls from the database of dll names
1687 my $input_file = $opt_output_directory."/dlls.db";
1688 my @dlls = `cat $input_file|sort|uniq`;
1690 if ($opt_output_format eq "h")
1692 # HTML gets a list of all the dlls. For docbook the index creates this for us
1693 push (@{$comment->{TEXT}},
1695 "The following dlls are provided by Wine:",
1698 # Add the dlls to the comment
1701 $_ =~ s/(\..*)?\n/\(\)/;
1702 push (@{$comment->{TEXT}}, $_, "");
1704 output_open_api_file("index");
1706 elsif ($opt_output_format eq "s")
1708 # Just write this as the initial blurb, with a chapter heading
1709 output_open_api_file("blurb");
1710 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1713 # Write out the document
1714 output_api_header($comment);
1715 output_api_comment($comment);
1716 output_api_footer($comment);
1717 if ($opt_output_format eq "s")
1719 print OUTPUT "</chapter>\n" # finish the chapter
1721 output_close_api_file();
1723 if ($opt_output_format eq "s")
1725 output_sgml_master_file(\@dlls);
1728 if ($opt_output_format eq "h")
1730 output_html_stylesheet();
1731 # FIXME: Create an alphabetical index
1736 # Write the master wine-api.sgml, linking it to each dll.
1737 sub output_sgml_master_file
1739 my $dlls = shift(@_);
1741 output_open_api_file("wine-api");
1742 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1743 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1744 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1749 $_ =~ s/(\..*)?\n//;
1750 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1753 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1754 print OUTPUT " &blurb;\n";
1758 print OUTPUT " &",$_,";\n"
1760 print OUTPUT "\n\n</book>\n";
1762 output_close_api_file();
1765 # Produce the sgml for the dll chapter from the generated files
1766 sub output_sgml_dll_file
1768 my $spec_details = shift(@_);
1770 # Make a list of all the documentation files to include
1771 my $exports = $spec_details->{EXPORTS};
1772 my @source_files = ();
1775 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1776 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1777 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1779 # A documented function
1780 push (@source_files,@$_[3]);
1784 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1786 @source_files = sort @source_files;
1788 # create a new chapter for this dll
1789 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1790 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1791 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1792 output_close_api_file();
1794 # Add the sorted documentation, cleaning up as we go
1795 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1798 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1799 `rm -f $opt_output_directory/$_.sgml`;
1802 # close the chapter, and overwite the dll source
1803 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1804 print OUTPUT "</chapter>\n";
1806 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1809 # Output the stylesheet for HTML output
1810 sub output_html_stylesheet
1812 if ($opt_output_format ne "h")
1818 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
1820 * Default styles for Wine HTML Documentation.
1822 * This style sheet should be altered to suit your needs/taste.
1824 BODY { /* Page body */
1825 background-color: white;
1827 font-family: Tahomsans-serif;
1831 a:link { color: #4444ff; } /* Links */
1832 a:visited { color: #333377 }
1833 a:active { color: #0000dd }
1834 H2.section { /* Section Headers */
1835 font-family: sans-serif;
1837 background-color: #F0F0FE;
1839 margin-right: 1.0in;
1841 b.func_name { /* Function Name */
1845 i.dll_ord { /* Italicised DLL+ordinal */
1847 font-family: sans-serif;
1850 p { /* Paragraphs */
1852 margin-right: 0.5in;
1854 table { /* tables */
1856 margin-right: 0.5in;
1858 pre.proto /* API Function prototype */
1860 border-style: solid;
1862 border-color: #777777;
1863 background-color: #F0F0BB;
1866 vertical-align: top;
1868 margin-right: 1.0in;
1870 tt.param { /* Parameter name */
1874 tt.const { /* Constant */
1877 i.in_out { /* In/Out */
1881 tt.coderef { /* Code in description text */
1884 b.emp /* Emphasis */ {
1888 i.footer { /* Footer */
1889 font-family: sans-serif;
1895 my $output_file = "$opt_output_directory/apidoc.css";
1896 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";