Make winapi_check handle spec files where no handler is specified.
[wine.git] / tools / c2man.pl
blob6d23bed76690d796dcaff95f4b2bc023517661c4
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 # 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?
29 use strict;
31 # Options
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
44 my %spec_files;
45 # All the collected details about all the source files being processed
46 my %source_files;
48 # useful globals
49 my $pwd = `pwd`."/";
50 $pwd =~ s/\n//;
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";
57 sub usage
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",
65 "Options:\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
75 if( @ARGV == 0)
77 usage();
80 # Process command line options
81 while(defined($_ = shift @ARGV))
83 if( s/^-// )
85 # An option.
86 for ($_)
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); }
100 last;
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."/"; };
107 last;
109 die "Unrecognised option $_\n";
112 else
114 # A source file.
115 push (@opt_source_file_list, $_);
119 # Remove duplicate include directories
120 my %htmp;
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};
152 for (@$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();
171 exit 0;
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;
182 my $spec_details =
184 NAME => $spec_name,
185 DLL_NAME => $dll_name,
186 NUM_EXPORTS => 0,
187 NUM_STUBS => 0,
188 NUM_FUNCS => 0,
189 NUM_FORWARDS => 0,
190 NUM_VARS => 0,
191 NUM_DOCS => 0,
192 CONTRIBUTORS => [ ],
193 SOURCES => [ ],
194 DESCRIPTION => [ ],
195 EXPORTS => [ ],
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;
211 while(<SPEC_FILE>)
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; };
233 /^(forward|extern)$/
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";
240 last;
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}++;
261 my $documented = 0;
262 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
263 push (@{$spec_details->{EXPORTS}},[@export]);
266 close(SPEC_FILE);
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(@_);
276 my $source_details =
278 CONTRIBUTORS => [ ],
279 DEBUG_CHANNEL => "",
281 my $comment =
283 FILE => $source_file,
284 COMMENT_NAME => "",
285 ALT_NAME => "",
286 DLL_NAME => "",
287 ORDINAL => "",
288 RETURNS => "",
289 PROTOTYPE => [],
290 TEXT => [],
292 my $parse_state = 0;
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];
305 while(<SOURCE_FILE>)
307 s/\n$//; # Strip newline
308 s/^\s+//; # Strip leading space
309 s/\s+$//; # Strip trailing space
310 if (! /^\*\|/ )
312 # Strip multiple tabs & spaces to a single space
313 s/\s+/ /g;
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
345 if ( /^\/\**$/ )
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;
356 $extra_comment = 0;
357 $parse_state = 3;
360 elsif ($parse_state == 1) # Reading in a comment
362 if ( /^\**\// )
364 # Found the end of the comment
365 $parse_state = 2;
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 "")
381 # Put it back
382 push (@{$comment->{TEXT}},$last_line);
384 if ( /^[A-Z]+$/ )
386 $ignore_blank_lines = 1;
388 else
390 $ignore_blank_lines = 0;
394 if ($ignore_blank_lines == 0 || $_ ne "")
396 push (@{$comment->{TEXT}},$_); # Add the comment text
399 else
401 # This isn't a well formatted comment: look for the next one
402 $parse_state = 0;
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
412 else
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";
427 $parse_state = 0;
431 elsif ($parse_state == 3) # Reading in the first line of a comment
433 s/^ *\** *//;
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
441 $parse_state = 1;
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} = "";
449 $extra_comment = 1;
450 $parse_state = 1;
452 else
454 # This isn't a well formatted comment: look for the next one
455 $parse_state = 0;
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 "")
471 # Put it back
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.");
480 $parse_state = 5;
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);
493 else
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";
503 $parse_state = 0;
506 close(SOURCE_FILE);
509 # Standardise a comments text for consistency
510 sub process_comment_text
512 my $comment = shift(@_);
514 for (@{$comment->{TEXT}})
516 if (! /^\|/ )
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
523 s/True|true/TRUE/g;
524 s/False|false/FALSE/g;
525 s/Null|null/NULL/g;
526 # Preferred capitalisations
527 s/ wine| WINE/ Wine/g;
528 s/ API | api / Api /g;
529 s/DLL| Dll /dll /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;
534 s/ ID | id / Id /g;
535 # Grammar
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
539 # Common idioms
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'
543 # Trademarks
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;
548 # Abbreviations
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.
558 sub process_comment
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";
572 return;
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";
598 return;
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}++;
645 @$export[4] |= 1;
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}})
707 if ( /^RETURNS$/ )
709 $found_returns = 1;
710 $in_description = 0;
712 elsif ( /^DESCRIPTION$/ )
714 $in_description = 1;
716 elsif ($in_description == 1)
718 if ( !/^[A-Z]+$/ )
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
723 $found_returns = 1;
725 $found_description_text = 1;
727 else
729 $in_description = 0;
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}--;
741 @$export[4] &= ~1;
742 return;
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
763 # Find header file
764 # FIXME: This sometimes gives the error "sh: <file>.h: Permission denied" - why?
765 my $h_file = "";
766 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
767 $tmp = `$tmp`;
768 my $exit_value = $? >> 8;
769 if ($exit_value == 0)
771 $tmp =~ s/\n.*//;
772 if ($tmp ne "")
774 $h_file = `basename $tmp`;
777 else
779 $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
780 $tmp = `$tmp`;
781 $exit_value = $? >> 8;
782 if ($exit_value == 0)
784 $tmp =~ s/\n.*//;
785 if ($tmp ne "")
787 $h_file = `basename $tmp`;
791 $h_file =~ s/^ *//;
792 $h_file =~ s/\n//;
793 if ($h_file eq "")
795 $h_file = "Not defined in a Wine header";
797 else
799 $h_file = "Defined in \"".$h_file."\"";
802 # Find source file
803 my $c_file = $comment->{FILE};
804 if ($opt_wine_root_dir ne "")
806 my $cfile = $pwd."/".$c_file; # Current dir + file
807 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
808 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
809 $cfile =~ s/\n//; # Strip newline
810 my $newfile = $c_file;
811 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
812 $cfile = $cfile."/".$newfile; # Append filename to base path
813 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
814 $cfile =~ s/\/\//\//g; # Remove any double slashes
815 $cfile =~ s/^\/+//; # Strip initial directory slash
816 $c_file = $cfile;
818 $c_file = "Implemented in \"".$c_file."\"";
820 # Add the implementation details
821 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
823 my $source_details = $source_files{$comment->{FILE}}[0];
824 if ($source_details->{DEBUG_CHANNEL} ne "")
826 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\"");
829 # Write out the documentation for the API
830 output_comment($comment)
833 # process our extra comment and output it if it is suitable.
834 sub process_extra_comment
836 my $comment = shift(@_);
838 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
840 if (!defined($spec_details))
842 if ($opt_verbose > 2)
844 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
845 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
847 return;
850 # Check first to see if this is documentation for the DLL.
851 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
853 if ($opt_verbose > 2)
855 print "Info: Found DLL documentation\n";
857 for (@{$comment->{TEXT}})
859 push (@{$spec_details->{DESCRIPTION}}, $_);
861 return;
864 # Add the comment to the DLL page as a link
865 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
867 # If we have a prototype, process as a regular comment
868 if (@{$comment->{PROTOTYPE}})
870 $comment->{ORDINAL} = "@";
872 # Add an index for the comment name
873 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
875 # Add a fake exported entry
876 $spec_details->{NUM_EXPORTS}++;
877 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
878 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
879 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
880 push (@{$spec_details->{EXPORTS}},[@export]);
881 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
882 process_comment($comment);
883 return;
886 if ($opt_verbose > 0)
888 print "Processing ",$comment->{COMMENT_NAME},"\n";
891 if (@{$spec_details->{CURRENT_EXTRA}})
893 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
895 if ($opt_verbose > 0)
897 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
899 # Output the current comment
900 process_comment_text($current_comment);
901 output_open_api_file($current_comment->{COMMENT_NAME});
902 output_api_header($current_comment);
903 output_api_name($current_comment);
904 output_api_comment($current_comment);
905 output_api_footer($current_comment);
906 output_close_api_file();
909 if ($opt_verbose > 2)
911 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
914 my $comment_copy =
916 FILE => $comment->{FILE},
917 COMMENT_NAME => $comment->{COMMENT_NAME},
918 ALT_NAME => $comment->{ALT_NAME},
919 DLL_NAME => $comment->{DLL_NAME},
920 ORDINAL => $comment->{ORDINAL},
921 RETURNS => $comment->{RETURNS},
922 PROTOTYPE => [],
923 TEXT => [],
926 for (@{$comment->{TEXT}})
928 push (@{$comment_copy->{TEXT}}, $_);
930 # Set this comment to be the current extra comment
931 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
934 # Write a standardised comment out in the appropriate format
935 sub output_comment
937 my $comment = shift(@_);
939 if ($opt_verbose > 0)
941 print "Processing ",$comment->{COMMENT_NAME},"\n";
944 if ($opt_verbose > 4)
946 print "--PROTO--\n";
947 for (@{$comment->{PROTOTYPE}})
949 print "'".$_."'\n";
952 print "--COMMENT--\n";
953 for (@{$comment->{TEXT} })
955 print $_."\n";
959 output_open_api_file($comment->{COMMENT_NAME});
960 output_api_header($comment);
961 output_api_name($comment);
962 output_api_synopsis($comment);
963 output_api_comment($comment);
964 output_api_footer($comment);
965 output_close_api_file();
968 # Write out an index file for each .spec processed
969 sub process_index_files
971 foreach my $spec_file (keys %spec_files)
973 my $spec_details = $spec_files{$spec_file}[0];
974 if (defined ($spec_details->{DLL_NAME}))
976 if (@{$spec_details->{CURRENT_EXTRA}})
978 # We have an unwritten extra comment, write it
979 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
980 process_extra_comment($current_comment);
981 @{$spec_details->{CURRENT_EXTRA}} = ();
983 output_spec($spec_details);
988 # Write a spec files documentation out in the appropriate format
989 sub output_spec
991 my $spec_details = shift(@_);
993 if ($opt_verbose > 2)
995 print "Writing:",$spec_details->{DLL_NAME},"\n";
998 # Use the comment output functions for consistency
999 my $comment =
1001 FILE => $spec_details->{DLL_NAME},
1002 COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1003 ALT_NAME => $spec_details->{DLL_NAME},
1004 DLL_NAME => "",
1005 ORDINAL => "",
1006 RETURNS => "",
1007 PROTOTYPE => [],
1008 TEXT => [],
1010 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1011 $spec_details->{NUM_FUNCS};
1012 my $percent_implemented = 0;
1013 if ($total_implemented)
1015 $percent_implemented = $total_implemented /
1016 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1018 $percent_implemented = int($percent_implemented);
1019 my $percent_documented = 0;
1020 if ($spec_details->{NUM_DOCS})
1022 # Treat forwards and data as documented funcs for statistics
1023 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1024 $percent_documented = int($percent_documented);
1027 # Make a list of the contributors to this DLL. Do this only for the source
1028 # files that make up the DLL, because some directories specify multiple dlls.
1029 my @contributors;
1031 for (@{$spec_details->{SOURCES}})
1033 my $source_details = $source_files{$_}[0];
1034 for (@{$source_details->{CONTRIBUTORS}})
1036 push (@contributors, $_);
1040 my %saw;
1041 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1042 @contributors = sort @contributors;
1044 # Remove duplicates and blanks
1045 for(my $i=0; $i<@contributors; $i++)
1047 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1049 $contributors[$i-1] = $contributors[$i];
1052 undef %saw;
1053 @contributors = grep(!$saw{$_}++, @contributors);
1055 if ($opt_verbose > 3)
1057 print "Contributors:\n";
1058 for (@contributors)
1060 print "'".$_."'\n";
1063 my $contribstring = join (", ", @contributors);
1065 # Create the initial comment text
1066 @{$comment->{TEXT}} = (
1067 "NAME",
1068 $comment->{COMMENT_NAME}
1071 # Add the description, if we have one
1072 if (@{$spec_details->{DESCRIPTION}})
1074 push (@{$comment->{TEXT}}, "DESCRIPTION");
1075 for (@{$spec_details->{DESCRIPTION}})
1077 push (@{$comment->{TEXT}}, $_);
1081 # Add the statistics and contributors
1082 push (@{$comment->{TEXT}},
1083 "STATISTICS",
1084 "Forwards: ".$spec_details->{NUM_FORWARDS},
1085 "Variables: ".$spec_details->{NUM_VARS},
1086 "Stubs: ".$spec_details->{NUM_STUBS},
1087 "Functions: ".$spec_details->{NUM_FUNCS},
1088 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1089 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1090 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1091 "CONTRIBUTORS",
1092 "The following people hold copyrights on the source files comprising this dll:",
1094 $contribstring,
1095 "Note: This list may not be complete.",
1096 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1100 if ($opt_output_format eq "h")
1102 # Add the exports to the comment text
1103 push (@{$comment->{TEXT}},"EXPORTS");
1104 my $exports = $spec_details->{EXPORTS};
1105 for (@$exports)
1107 my $line = "";
1109 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1110 if (@$_[1] eq "forward")
1112 my $forward_dll = @$_[3];
1113 $forward_dll =~ s/\.(.*)//;
1114 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1116 elsif (@$_[1] eq "extern")
1118 $line = @$_[2]." (extern)";
1120 elsif (@$_[1] eq "stub")
1122 $line = @$_[2]." (stub)";
1124 elsif (@$_[1] eq "fake")
1126 # Don't add this function here, it gets listed with the extra documentation
1128 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1130 $line = @$_[2]." (data)";
1132 else
1134 # A function
1135 if (@$_[4] & 1)
1137 # Documented
1138 $line = @$_[2]." (implemented as ".@$_[3]."())";
1139 if (@$_[2] ne @$_[3])
1141 $line = @$_[2]." (implemented as ".@$_[3]."())";
1143 else
1145 $line = @$_[2]."()";
1148 else
1150 $line = @$_[2]." (not documented)";
1153 if ($line ne "")
1155 push (@{$comment->{TEXT}}, $line, "");
1159 # Add links to the extra documentation
1160 if (@{$spec_details->{EXTRA_COMMENTS}})
1162 push (@{$comment->{TEXT}}, "SEE ALSO");
1163 my %htmp;
1164 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1165 for (@{$spec_details->{EXTRA_COMMENTS}})
1167 push (@{$comment->{TEXT}}, $_."()", "");
1171 # Write out the document
1172 output_open_api_file($spec_details->{DLL_NAME});
1173 output_api_header($comment);
1174 output_api_comment($comment);
1175 output_api_footer($comment);
1176 output_close_api_file();
1178 # Add this dll to the database of dll names
1179 my $output_file = $opt_output_directory."/dlls.db";
1181 # Append the dllname to the output db of names
1182 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1183 print DLLDB $spec_details->{DLL_NAME},"\n";
1184 close(DLLDB);
1186 if ($opt_output_format eq "s")
1188 output_sgml_dll_file($spec_details);
1189 return;
1194 # OUTPUT FUNCTIONS
1195 # ----------------
1196 # Only these functions know anything about formatting for a specific
1197 # output type. The functions above work only with plain text.
1198 # This is to allow new types of output to be added easily.
1200 # Open the api file
1201 sub output_open_api_file
1203 my $output_name = shift(@_);
1204 $output_name = $opt_output_directory."/".$output_name;
1206 if ($opt_output_format eq "h")
1208 $output_name = $output_name.".html";
1210 elsif ($opt_output_format eq "s")
1212 $output_name = $output_name.".sgml";
1214 else
1216 $output_name = $output_name.".".$opt_manual_section;
1218 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1221 # Close the api file
1222 sub output_close_api_file
1224 close (OUTPUT);
1227 # Output the api file header
1228 sub output_api_header
1230 my $comment = shift(@_);
1232 if ($opt_output_format eq "h")
1234 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1235 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1236 print OUTPUT "<HTML>\n<HEAD>\n";
1237 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1238 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1239 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1240 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1242 elsif ($opt_output_format eq "s")
1244 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1245 "<sect1>\n",
1246 "<title>$comment->{COMMENT_NAME}</title>\n";
1248 else
1250 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1251 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1252 "Wine API\" \"Wine API\"\n";
1256 sub output_api_footer
1258 if ($opt_output_format eq "h")
1260 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1261 "Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1262 "Generated $date</i></p>\n</body>\n</html>\n";
1264 elsif ($opt_output_format eq "s")
1266 print OUTPUT "</sect1>\n";
1267 return;
1269 else
1274 sub output_api_section_start
1276 my $comment = shift(@_);
1277 my $section_name = shift(@_);
1279 if ($opt_output_format eq "h")
1281 print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1283 elsif ($opt_output_format eq "s")
1285 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1287 else
1289 print OUTPUT "\n\.SH ",$section_name,"\n";
1293 sub output_api_section_end
1295 # Not currently required by any output formats
1298 sub output_api_name
1300 my $comment = shift(@_);
1302 output_api_section_start($comment,"NAME");
1304 my $dll_ordinal = "";
1305 if ($comment->{ORDINAL} ne "")
1307 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1309 if ($opt_output_format eq "h")
1311 print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1312 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1313 ,$dll_ordinal,"</i></p>\n";
1315 elsif ($opt_output_format eq "s")
1317 print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>",
1318 $dll_ordinal,"</emphasis>\n</para>\n";
1320 else
1322 print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1325 output_api_section_end();
1328 sub output_api_synopsis
1330 my $comment = shift(@_);
1331 my @fmt;
1333 output_api_section_start($comment,"SYNOPSIS");
1335 if ($opt_output_format eq "h")
1337 print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1338 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1340 elsif ($opt_output_format eq "s")
1342 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1343 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1345 else
1347 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1348 @fmt = ("", "\n", "\\fI", "\\fR");
1351 # Since our prototype is output in a pre-formatted block, line up the
1352 # parameters and parameter comments in the same column.
1354 # First caluculate where the columns should start
1355 my $biggest_length = 0;
1356 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1358 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1359 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1361 my $length = length $1;
1362 if ($length > $biggest_length)
1364 $biggest_length = $length;
1369 # Now pad the string with blanks
1370 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1372 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1373 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1375 my $pad_len = $biggest_length - length $1;
1376 my $padding = " " x ($pad_len);
1377 ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1381 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1383 # Format the parameter name
1384 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1385 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1386 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1387 print OUTPUT $line;
1390 if ($opt_output_format eq "h")
1392 print OUTPUT " )\n\n</pre></p>\n";
1394 elsif ($opt_output_format eq "s")
1396 print OUTPUT " )\n</screen>\n";
1398 else
1400 print OUTPUT " )\n";
1403 output_api_section_end();
1406 sub output_api_comment
1408 my $comment = shift(@_);
1409 my $open_paragraph = 0;
1410 my $open_raw = 0;
1411 my $param_docs = 0;
1412 my @fmt;
1414 if ($opt_output_format eq "h")
1416 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1417 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1418 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1419 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1420 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1422 elsif ($opt_output_format eq "s")
1424 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1425 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1426 "<screen>\n","</screen>\n",
1427 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1428 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1429 "</entry>","</entry><entry>");
1431 else
1433 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1434 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1437 # Extract the parameter names
1438 my @parameter_names;
1439 for (@{$comment->{PROTOTYPE}})
1441 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1443 push (@parameter_names, $2);
1447 for (@{$comment->{TEXT}})
1449 if ($opt_output_format eq "h" || $opt_output_format eq "s")
1451 # Map special characters
1452 s/\&/\&amp;/g;
1453 s/\</\&lt;/g;
1454 s/\>/\&gt;/g;
1455 s/\([Cc]\)/\&copy;/g;
1456 s/\(tm\)/&#174;/;
1459 if ( s/^\|// )
1461 # Raw output
1462 if ($open_raw == 0)
1464 if ($open_paragraph == 1)
1466 # Close the open paragraph
1467 print OUTPUT $fmt[1];
1468 $open_paragraph = 0;
1470 # Start raw output
1471 print OUTPUT $fmt[12];
1472 $open_raw = 1;
1474 if ($opt_output_format eq "")
1476 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1478 print OUTPUT $_,"\n";
1480 else
1482 # Highlight strings
1483 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1484 # Highlight literal chars
1485 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1486 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1487 # Highlight numeric constants
1488 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1490 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1491 # FIXME: Using bullet points for leading '-' would look nicer.
1492 if ($open_paragraph == 1)
1494 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1495 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1497 else
1499 s/^(\-)/$fmt[4]$1$fmt[5]/;
1500 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1503 if ($opt_output_format eq "h")
1505 # Html uses links for API calls
1506 s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1507 # And references to COM objects (hey, they'll get documented one day)
1508 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1509 # Convert any web addresses to real links
1510 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1512 else
1514 if ($opt_output_format eq "")
1516 # Give the man section for API calls
1517 s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1519 else
1521 # Highlight API calls
1522 s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1525 # And references to COM objects
1526 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1529 if ($open_raw == 1)
1531 # Finish the raw output
1532 print OUTPUT $fmt[13];
1533 $open_raw = 0;
1536 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1538 # Start of a new section
1539 if ($open_paragraph == 1)
1541 if ($param_docs == 1)
1543 print OUTPUT $fmt[17],$fmt[15];
1545 else
1547 print OUTPUT $fmt[1];
1549 $open_paragraph = 0;
1551 output_api_section_start($comment,$_);
1552 if ( /^PARAMS$/ )
1554 print OUTPUT $fmt[14];
1555 $param_docs = 1;
1557 else
1559 #print OUTPUT $fmt[15];
1560 $param_docs = 0;
1563 elsif ( /^$/ )
1565 # Empty line, indicating a new paragraph
1566 if ($open_paragraph == 1)
1568 if ($param_docs == 0)
1570 print OUTPUT $fmt[1];
1571 $open_paragraph = 0;
1575 else
1577 if ($param_docs == 1)
1579 if ($open_paragraph == 1)
1581 # For parameter docs, put each parameter into a new paragraph/table row
1582 print OUTPUT $fmt[17];
1583 $open_paragraph = 0;
1585 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19]/; # Format In/Out
1587 else
1589 # Within paragraph lines, prevent lines running together
1590 $_ = $_." ";
1593 # Format parameter names where they appear in the comment
1594 for my $parameter_name (@parameter_names)
1596 s/(^|[ \.\,\(\-])($parameter_name)($|[ \.\)\,\-])/$1$fmt[8]$2$fmt[9]$3/g;
1599 if ($open_paragraph == 0)
1601 if ($param_docs == 1)
1603 print OUTPUT $fmt[16];
1605 else
1607 print OUTPUT $fmt[0];
1609 $open_paragraph = 1;
1611 # Anything in all uppercase on its own gets emphasised
1612 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1614 print OUTPUT $_;
1618 if ($open_raw == 1)
1620 print OUTPUT $fmt[13];
1622 if ($open_paragraph == 1)
1624 print OUTPUT $fmt[1];
1628 # Create the master index file
1629 sub output_master_index_files
1631 if ($opt_output_format eq "")
1633 return; # No master index for man pages
1636 # Use the comment output functions for consistency
1637 my $comment =
1639 FILE => "",
1640 COMMENT_NAME => "The Wine Api Guide",
1641 ALT_NAME => "The Wine Api Guide",
1642 DLL_NAME => "",
1643 ORDINAL => "",
1644 RETURNS => "",
1645 PROTOTYPE => [],
1646 TEXT => [],
1649 if ($opt_output_format eq "s")
1651 $comment->{COMMENT_NAME} = "Introduction";
1652 $comment->{ALT_NAME} = "Introduction",
1654 elsif ($opt_output_format eq "h")
1656 @{$comment->{TEXT}} = (
1657 "NAME",
1658 $comment->{COMMENT_NAME},
1659 "INTRODUCTION",
1663 # Create the initial comment text
1664 push (@{$comment->{TEXT}},
1665 "This document describes the Api calls made available",
1666 "by Wine. They are grouped by the dll that exports them.",
1668 "Please do not edit this document, since it is generated automatically",
1669 "from the Wine source code tree. Details on updating this documentation",
1670 "are given in the \"Wine Developers Guide\".",
1671 "CONTRIBUTORS",
1672 "Api documentation is generally written by the person who ",
1673 "implements a given Api call. Authors of each dll are listed in the overview ",
1674 "section for that dll. Additional contributors who have updated source files ",
1675 "but have not entered their names in a copyright statement are noted by an ",
1676 "entry in the file \"Changelog\" from the Wine source code distribution.",
1680 # Read in all dlls from the database of dll names
1681 my $input_file = $opt_output_directory."/dlls.db";
1682 my @dlls = `cat $input_file|sort|uniq`;
1684 if ($opt_output_format eq "h")
1686 # HTML gets a list of all the dlls. For docbook the index creates this for us
1687 push (@{$comment->{TEXT}},
1688 "DLLS",
1689 "The following dlls are provided by Wine:",
1692 # Add the dlls to the comment
1693 for (@dlls)
1695 $_ =~ s/(\..*)?\n/\(\)/;
1696 push (@{$comment->{TEXT}}, $_, "");
1698 output_open_api_file("index");
1700 elsif ($opt_output_format eq "s")
1702 # Just write this as the initial blurb, with a chapter heading
1703 output_open_api_file("blurb");
1704 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1707 # Write out the document
1708 output_api_header($comment);
1709 output_api_comment($comment);
1710 output_api_footer($comment);
1711 if ($opt_output_format eq "s")
1713 print OUTPUT "</chapter>\n" # finish the chapter
1715 output_close_api_file();
1717 if ($opt_output_format eq "s")
1719 output_sgml_master_file(\@dlls);
1720 return;
1722 if ($opt_output_format eq "h")
1724 output_html_stylesheet();
1725 # FIXME: Create an alphabetical index
1726 return;
1730 # Write the master wine-api.sgml, linking it to each dll.
1731 sub output_sgml_master_file
1733 my $dlls = shift(@_);
1735 output_open_api_file("wine-api");
1736 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1737 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1738 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1740 # List the entities
1741 for (@$dlls)
1743 $_ =~ s/(\..*)?\n//;
1744 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1747 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1748 print OUTPUT " &blurb;\n";
1750 for (@$dlls)
1752 print OUTPUT " &",$_,";\n"
1754 print OUTPUT "\n\n</book>\n";
1756 output_close_api_file();
1759 # Produce the sgml for the dll chapter from the generated files
1760 sub output_sgml_dll_file
1762 my $spec_details = shift(@_);
1764 # Make a list of all the documentation files to include
1765 my $exports = $spec_details->{EXPORTS};
1766 my @source_files = ();
1767 for (@$exports)
1769 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1770 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1771 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1773 # A documented function
1774 push (@source_files,@$_[3]);
1778 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1780 @source_files = sort @source_files;
1782 # create a new chapter for this dll
1783 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1784 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1785 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1786 output_close_api_file();
1788 # Add the sorted documentation, cleaning up as we go
1789 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1790 for (@source_files)
1792 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1793 `rm -f $opt_output_directory/$_.sgml`;
1796 # close the chapter, and overwite the dll source
1797 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1798 print OUTPUT "</chapter>\n";
1799 close OUTPUT;
1800 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1803 # Output the stylesheet for HTML output
1804 sub output_html_stylesheet
1806 if ($opt_output_format ne "h")
1808 return;
1811 my $css;
1812 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
1814 * Default styles for Wine HTML Documentation.
1816 * This style sheet should be altered to suit your needs/taste.
1818 BODY { /* Page body */
1819 background-color: white;
1820 color: black;
1821 font-family: Tahomsans-serif;
1822 font-style: normal;
1823 font-size: 10pt;
1825 a:link { color: #4444ff; } /* Links */
1826 a:visited { color: #333377 }
1827 a:active { color: #0000dd }
1828 H2.section { /* Section Headers */
1829 font-family: sans-serif;
1830 color: #777777;
1831 background-color: #F0F0FE;
1832 margin-left: 0.2in;
1833 margin-right: 1.0in;
1835 b.func_name { /* Function Name */
1836 font-size: 10pt;
1837 font-style: bold;
1839 i.dll_ord { /* Italicised DLL+ordinal */
1840 color: #888888;
1841 font-family: sans-serif;
1842 font-size: 8pt;
1844 p { /* Paragraphs */
1845 margin-left: 0.5in;
1846 margin-right: 0.5in;
1848 table { /* tables */
1849 margin-left: 0.5in;
1850 margin-right: 0.5in;
1852 pre.proto /* API Function prototype */
1854 border-style: solid;
1855 border-width: 1px;
1856 border-color: #777777;
1857 background-color: #F0F0BB;
1858 color: black;
1859 font-size: 10pt;
1860 vertical-align: top;
1861 margin-left: 0.5in;
1862 margin-right: 1.0in;
1864 tt.param { /* Parameter name */
1865 font-style: italic;
1866 color: blue;
1868 tt.const { /* Constant */
1869 color: red;
1871 i.in_out { /* In/Out */
1872 font-size: 8pt;
1873 color: grey;
1875 tt.coderef { /* Code in description text */
1876 color: darkgreen;
1878 b.emp /* Emphasis */ {
1879 font-style: bold;
1880 color: darkblue;
1882 i.footer { /* Footer */
1883 font-family: sans-serif;
1884 font-size: 6pt;
1885 color: darkgrey;
1887 HERE_TARGET
1889 my $output_file = "$opt_output_directory/apidoc.css";
1890 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
1891 print CSS $css;
1892 close(CSS);