Added 32 bit sample size tests and 4 and 6 channel tests.
[wine/multimedia.git] / tools / c2man.pl
blob5744958df392a84b584106c9e69a8203f25283ae
1 #! /usr/bin/perl -w
3 # Generate API documentation. See documentation/documentation.sgml for details.
5 # Copyright (C) 2000 Mike McCormack
6 # Copyright (C) 2003 Jon Griffiths
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 # TODO
23 # SGML gurus - feel free to smarten up the SGML.
24 # Add any other relevant information for the dll - imports etc
25 # Should we have a special output mode for WineHQ?
27 use strict;
28 use bytes;
30 # Function flags. most of these come from the spec flags
31 my $FLAG_DOCUMENTED = 1;
32 my $FLAG_NONAME = 2;
33 my $FLAG_I386 = 4;
34 my $FLAG_REGISTER = 8;
35 my $FLAG_APAIR = 16; # The A version of a matching W function
36 my $FLAG_WPAIR = 32; # The W version of a matching A function
37 my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function
40 # Options
41 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
42 my $opt_manual_section = "3w";
43 my $opt_wine_root_dir = "";
44 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml
45 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
46 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
47 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
48 my @opt_header_file_list = ();
49 my @opt_spec_file_list = ();
50 my @opt_source_file_list = ();
52 # All the collected details about all the .spec files being processed
53 my %spec_files;
54 # All the collected details about all the source files being processed
55 my %source_files;
56 # All documented functions that are to be placed in the index
57 my @index_entries_list = ();
59 # useful globals
60 my $pwd = `pwd`."/";
61 $pwd =~ s/\n//;
62 my @datetime = localtime;
63 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
64 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
65 my $year = $datetime[5] + 1900;
66 my $date = "$months[$datetime[4]] $year";
68 sub usage
70 print "\nCreate API Documentation from Wine source code.\n\n",
71 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
72 "Where: <spec> is a .spec file giving a DLL's exports.\n",
73 " <include> is an include directory used by the DLL.\n",
74 " <source> is a source file of the DLL.\n",
75 " The above can be given multiple times on the command line, as appropriate.\n",
76 "Options:\n",
77 " -Th : Output HTML instead of a man page\n",
78 " -Ts : Output SGML (Docbook source) instead of a man page\n",
79 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
80 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
81 " -e : Output \"FIXME\" documentation from empty comments.\n",
82 " -v : Verbosity. Can be given more than once for more detail.\n";
85 # Print usage if we're called with no args
86 if( @ARGV == 0)
88 usage();
91 # Process command line options
92 while(defined($_ = shift @ARGV))
94 if( s/^-// )
96 # An option.
97 for ($_)
99 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
100 s/^S// && do { $opt_manual_section = $_; last; };
101 /^Th$/ && do { $opt_output_format = "h"; last; };
102 /^Ts$/ && do { $opt_output_format = "s"; last; };
103 /^v$/ && do { $opt_verbose++; last; };
104 /^e$/ && do { $opt_output_empty = 1; last; };
105 /^L$/ && do { last; };
106 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
107 s/^I// && do { if ($_ ne ".") {
108 my $include = $_."/*.h";
109 $include =~ s/\/\//\//g;
110 my $have_headers = `ls $include >/dev/null 2>&1`;
111 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
113 last;
115 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
116 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
117 $opt_wine_root_dir =~ s/\n//;
118 $opt_wine_root_dir =~ s/\/\//\//g;
119 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
120 last;
122 die "Unrecognised option $_\n";
125 else
127 # A source file.
128 push (@opt_source_file_list, $_);
132 # Remove duplicate include directories
133 my %htmp;
134 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
136 if ($opt_verbose > 3)
138 print "Output dir:'".$opt_output_directory."'\n";
139 print "Section :'".$opt_manual_section."'\n";
140 print "Format :'".$opt_output_format."'\n";
141 print "Root :'".$opt_wine_root_dir."'\n";
142 print "Spec files:'@opt_spec_file_list'\n";
143 print "Includes :'@opt_header_file_list'\n";
144 print "Sources :'@opt_source_file_list'\n";
147 if (@opt_spec_file_list == 0)
149 exit 0; # Don't bother processing non-dll files
152 # Read in each .spec files exports and other details
153 while(my $spec_file = shift @opt_spec_file_list)
155 process_spec_file($spec_file);
158 if ($opt_verbose > 3)
160 foreach my $spec_file ( keys %spec_files )
162 print "in '$spec_file':\n";
163 my $spec_details = $spec_files{$spec_file}[0];
164 my $exports = $spec_details->{EXPORTS};
165 for (@$exports)
167 print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n";
172 # Extract and output the comments from each source file
173 while(defined($_ = shift @opt_source_file_list))
175 process_source_file($_);
178 # Write the index files for each spec
179 process_index_files();
181 # Write the master index file
182 output_master_index_files();
184 exit 0;
187 # Generate the list of exported entries for the dll
188 sub process_spec_file
190 my $spec_name = shift(@_);
191 my $dll_name = $spec_name;
192 $dll_name =~ s/\..*//; # Strip the file extension
193 my $uc_dll_name = uc $dll_name;
195 my $spec_details =
197 NAME => $spec_name,
198 DLL_NAME => $dll_name,
199 NUM_EXPORTS => 0,
200 NUM_STUBS => 0,
201 NUM_FUNCS => 0,
202 NUM_FORWARDS => 0,
203 NUM_VARS => 0,
204 NUM_DOCS => 0,
205 CONTRIBUTORS => [ ],
206 SOURCES => [ ],
207 DESCRIPTION => [ ],
208 EXPORTS => [ ],
209 EXPORTED_NAMES => { },
210 IMPLEMENTATION_NAMES => { },
211 EXTRA_COMMENTS => [ ],
212 CURRENT_EXTRA => [ ] ,
215 if ($opt_verbose > 0)
217 print "Processing ".$spec_name."\n";
220 # We allow opening to fail just to cater for the peculiarities of
221 # the Wine build system. This doesn't hurt, in any case
222 open(SPEC_FILE, "<$spec_name") || return;
224 while(<SPEC_FILE>)
226 s/^\s+//; # Strip leading space
227 s/\s+\n$/\n/; # Strip trailing space
228 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
229 s/\s*#.*//; # Strip comments
230 s/\(.*\)/ /; # Strip arguments
231 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
232 s/\n$//; # Strip newline
234 my $flags = 0;
235 if( /\-noname/ )
237 $flags |= $FLAG_NONAME;
239 if( /\-i386/ )
241 $flags |= $FLAG_I386;
243 if( /\-register/ )
245 $flags |= $FLAG_REGISTER;
247 s/ \-[a-z0-9]+//g; # Strip flags
249 if( /^(([0-9]+)|@) / )
251 # This line contains an exported symbol
252 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
254 for ($call_convention)
256 /^(cdecl|stdcall|varargs|pascal)$/
257 && do { $spec_details->{NUM_FUNCS}++; last; };
258 /^(variable|equate)$/
259 && do { $spec_details->{NUM_VARS}++; last; };
260 /^(extern)$/
261 && do { $spec_details->{NUM_FORWARDS}++; last; };
262 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
263 if ($opt_verbose > 0)
265 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
267 last;
270 # Convert ordinal only names so we can find them later
271 if ($exported_name eq "@")
273 $exported_name = $uc_dll_name.'_'.$ordinal;
275 if (!defined($implementation_name))
277 $implementation_name = $exported_name;
279 if ($implementation_name eq "")
281 $implementation_name = $exported_name;
284 if ($implementation_name =~ /(.*?)\./)
286 $call_convention = "forward"; # Referencing a function from another dll
287 $spec_details->{NUM_FUNCS}--;
288 $spec_details->{NUM_FORWARDS}++;
291 # Add indices for the exported and implementation names
292 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
293 if ($implementation_name ne $exported_name)
295 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
298 # Add the exported entry
299 $spec_details->{NUM_EXPORTS}++;
300 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
301 push (@{$spec_details->{EXPORTS}},[@export]);
304 close(SPEC_FILE);
306 # Add this .spec files details to the list of .spec files
307 $spec_files{$uc_dll_name} = [$spec_details];
310 # Read each source file, extract comments, and generate API documentation if appropriate
311 sub process_source_file
313 my $source_file = shift(@_);
314 my $source_details =
316 CONTRIBUTORS => [ ],
317 DEBUG_CHANNEL => "",
319 my $comment =
321 FILE => $source_file,
322 COMMENT_NAME => "",
323 ALT_NAME => "",
324 DLL_NAME => "",
325 ORDINAL => "",
326 RETURNS => "",
327 PROTOTYPE => [],
328 TEXT => [],
330 my $parse_state = 0;
331 my $ignore_blank_lines = 1;
332 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
334 if ($opt_verbose > 0)
336 print "Processing ".$source_file."\n";
338 open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n";
340 # Add this source file to the list of source files
341 $source_files{$source_file} = [$source_details];
343 while(<SOURCE_FILE>)
345 s/\n$//; # Strip newline
346 s/^\s+//; # Strip leading space
347 s/\s+$//; # Strip trailing space
348 if (! /^\*\|/ )
350 # Strip multiple tabs & spaces to a single space
351 s/\s+/ /g;
354 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
356 # Extract a contributor to this file
357 my $contributor = $2;
358 $contributor =~ s/ *$//;
359 $contributor =~ s/^by //;
360 $contributor =~ s/\.$//;
361 $contributor =~ s/ (for .*)/ \($1\)/;
362 if ($contributor ne "")
364 if ($opt_verbose > 3)
366 print "Info: Found contributor:'".$contributor."'\n";
368 push (@{$source_details->{CONTRIBUTORS}},$contributor);
371 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
373 # Extract the debug channel to use
374 if ($opt_verbose > 3)
376 print "Info: Found debug channel:'".$1."'\n";
378 $source_details->{DEBUG_CHANNEL} = $1;
381 if ($parse_state == 0) # Searching for a comment
383 if ( /^\/\**$/ )
385 # Found a comment start
386 $comment->{COMMENT_NAME} = "";
387 $comment->{ALT_NAME} = "";
388 $comment->{DLL_NAME} = "";
389 $comment->{ORDINAL} = "";
390 $comment->{RETURNS} = "";
391 $comment->{PROTOTYPE} = [];
392 $comment->{TEXT} = [];
393 $ignore_blank_lines = 1;
394 $extra_comment = 0;
395 $parse_state = 3;
398 elsif ($parse_state == 1) # Reading in a comment
400 if ( /^\**\// )
402 # Found the end of the comment
403 $parse_state = 2;
405 elsif ( s/^\*\|/\|/ )
407 # A line of comment not meant to be pre-processed
408 push (@{$comment->{TEXT}},$_); # Add the comment text
410 elsif ( s/^ *\** *// )
412 # A line of comment, starting with an asterisk
413 if ( /^[A-Z]+$/ || $_ eq "")
415 # This is a section start, so skip blank lines before and after it.
416 my $last_line = pop(@{$comment->{TEXT}});
417 if (defined($last_line) && $last_line ne "")
419 # Put it back
420 push (@{$comment->{TEXT}},$last_line);
422 if ( /^[A-Z]+$/ )
424 $ignore_blank_lines = 1;
426 else
428 $ignore_blank_lines = 0;
432 if ($ignore_blank_lines == 0 || $_ ne "")
434 push (@{$comment->{TEXT}},$_); # Add the comment text
437 else
439 # This isn't a well formatted comment: look for the next one
440 $parse_state = 0;
443 elsif ($parse_state == 2) # Finished reading in a comment
445 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
446 /.*?\(/ )
448 # Comment is followed by a function definition
449 $parse_state = 4; # Fall through to read prototype
451 else
453 # Allow cpp directives and blank lines between the comment and prototype
454 if ($extra_comment == 1)
456 # An extra comment not followed by a function definition
457 $parse_state = 5; # Fall through to process comment
459 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
461 # This isn't a well formatted comment: look for the next one
462 if ($opt_verbose > 1)
464 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
466 $parse_state = 0;
470 elsif ($parse_state == 3) # Reading in the first line of a comment
472 s/^ *\** *//;
473 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
475 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
476 $comment->{COMMENT_NAME} = $1;
477 $comment->{DLL_NAME} = uc $3;
478 $comment->{ORDINAL} = $4;
479 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
480 $parse_state = 1;
482 elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ )
484 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
485 $comment->{COMMENT_NAME} = $1;
486 $comment->{DLL_NAME} = uc $2;
487 $comment->{ORDINAL} = "";
488 $extra_comment = 1;
489 $parse_state = 1;
491 else
493 # This isn't a well formatted comment: look for the next one
494 $parse_state = 0;
498 if ($parse_state == 4) # Reading in the function definition
500 push (@{$comment->{PROTOTYPE}},$_);
501 # Strip comments from the line before checking for ')'
502 my $stripped_line = $_;
503 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
504 if ( $stripped_line =~ /\)/ )
506 # Strip a blank last line
507 my $last_line = pop(@{$comment->{TEXT}});
508 if (defined($last_line) && $last_line ne "")
510 # Put it back
511 push (@{$comment->{TEXT}},$last_line);
514 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
516 # Create a 'not implemented' comment
517 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
519 $parse_state = 5;
523 if ($parse_state == 5) # Processing the comment
525 # Process it, if it has any text
526 if (@{$comment->{TEXT}} > 0)
528 if ($extra_comment == 1)
530 process_extra_comment($comment);
532 else
534 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
535 process_comment($comment);
538 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
540 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
542 $parse_state = 0;
545 close(SOURCE_FILE);
548 # Standardise a comments text for consistency
549 sub process_comment_text
551 my $comment = shift(@_);
552 my $i = 0;
554 for (@{$comment->{TEXT}})
556 if (! /^\|/ )
558 # Map I/O values. These come in too many formats to standardise now....
559 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
560 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
561 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
562 # TRUE/FALSE/NULL are defines, capitilise them
563 s/True|true/TRUE/g;
564 s/False|false/FALSE/g;
565 s/Null|null/NULL/g;
566 # Preferred capitalisations
567 s/ wine| WINE/ Wine/g;
568 s/ API | api / Api /g;
569 s/DLL|Dll/dll /g;
570 s/ URL | url / Url /g;
571 s/WIN16|win16/Win16/g;
572 s/WIN32|win32/Win32/g;
573 s/WIN64|win64/Win64/g;
574 s/ ID | id / Id /g;
575 # Grammar
576 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
577 s/ \:/\:/g; # Colons to the left
578 s/ \;/\;/g; # Semi-colons too
579 # Common idioms
580 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
581 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
582 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
583 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
584 # Trademarks
585 s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g;
586 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
587 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
588 s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
589 # Abbreviations
590 s/( char )/ character /g;
591 s/( chars )/ characters /g;
592 s/( info )/ information /g;
593 s/( app )/ application /g;
594 s/( apps )/ applications /g;
595 s/( exe )/ executable /g;
596 s/( ptr )/ pointer /g;
597 s/( obj )/ object /g;
598 s/( err )/ error /g;
599 s/( bool )/ boolean /g;
600 # Punctuation
601 if ( /\[I|\[O/ && ! /\.$/ )
603 $_ = $_."."; # Always have a full stop at the end of parameter desc.
605 elsif ($i > 0 && /^[A-Z]*$/ &&
606 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
607 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
610 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
612 # Paragraphs always end with a full stop
613 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
617 $i++;
621 # Standardise our comment and output it if it is suitable.
622 sub process_comment
624 my $comment = shift(@_);
626 # Don't process this comment if the function isn't exported
627 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
629 if (!defined($spec_details))
631 if ($opt_verbose > 2)
633 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
634 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
636 return;
639 if ($comment->{COMMENT_NAME} eq "@")
641 my $found = 0;
643 # Find the name from the .spec file
644 for (@{$spec_details->{EXPORTS}})
646 if (@$_[0] eq $comment->{ORDINAL})
648 $comment->{COMMENT_NAME} = @$_[2];
649 $found = 1;
653 if ($found == 0)
655 # Create an implementation name
656 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
660 my $exported_names = $spec_details->{EXPORTED_NAMES};
661 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
662 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
664 if (!defined($export_index))
666 # Perhaps the comment uses the implementation name?
667 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
669 if (!defined($export_index))
671 # This function doesn't appear to be exported. hmm.
672 if ($opt_verbose > 2)
674 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
675 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
677 return;
680 # When the function is exported twice we have the second name below the first
681 # (you see this a lot in ntdll, but also in some other places).
682 my $first_line = ${@{$comment->{TEXT}}}[1];
684 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
686 # Found a second name - mark it as documented
687 my $alt_index = $exported_names->{$1};
688 if (defined($alt_index))
690 if ($opt_verbose > 2)
692 print "Info: Found alternate name '",$1,"\n";
694 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
695 @$alt_export[4] |= $FLAG_DOCUMENTED;
696 $spec_details->{NUM_DOCS}++;
697 ${@{$comment->{TEXT}}}[1] = "";
701 if (@{$spec_details->{CURRENT_EXTRA}})
703 # We have an extra comment that might be related to this one
704 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
705 my $current_name = $current_comment->{COMMENT_NAME};
706 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
708 if ($opt_verbose > 2)
710 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
712 # Add a reference to this comment to our extra comment
713 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
717 # We want our docs generated using the implementation name, so they are unique
718 my $export = @{$spec_details->{EXPORTS}}[$export_index];
719 $comment->{COMMENT_NAME} = @$export[3];
720 $comment->{ALT_NAME} = @$export[2];
722 # Mark the function as documented
723 $spec_details->{NUM_DOCS}++;
724 @$export[4] |= $FLAG_DOCUMENTED;
726 # This file is used by the DLL - Make sure we get our contributors right
727 push (@{$spec_details->{SOURCES}},$comment->{FILE});
729 # If we have parameter comments in the prototype, extract them
730 my @parameter_comments;
731 for (@{$comment->{PROTOTYPE}})
733 s/ *\, */\,/g; # Strip spaces from around commas
735 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
737 my $parameter_comment = $2;
738 if (!$parameter_comment =~ /^\[/ )
740 # Add [IO] markers so we format the comment correctly
741 $parameter_comment = "[fixme] ".$parameter_comment;
743 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
745 # Add the parameter name
746 $parameter_comment = $2." ".$parameter_comment;
748 push (@parameter_comments, $parameter_comment);
752 # If we extracted any prototype comments, add them to the comment text.
753 if (@parameter_comments)
755 @parameter_comments = ("PARAMS", @parameter_comments);
756 my @new_comment = ();
757 my $inserted_params = 0;
759 for (@{$comment->{TEXT}})
761 if ( $inserted_params == 0 && /^[A-Z]+$/ )
763 # Found a section header, so this is where we insert
764 push (@new_comment, @parameter_comments);
765 $inserted_params = 1;
767 push (@new_comment, $_);
769 if ($inserted_params == 0)
771 # Add them to the end
772 push (@new_comment, @parameter_comments);
774 $comment->{TEXT} = [@new_comment];
777 if ($opt_fussy == 1 && $opt_output_empty == 0)
779 # Reject any comment that doesn't have a description or a RETURNS section.
780 # This is the default for now, 'coz many comments aren't suitable.
781 my $found_returns = 0;
782 my $found_description_text = 0;
783 my $in_description = 0;
784 for (@{$comment->{TEXT}})
786 if ( /^RETURNS$/ )
788 $found_returns = 1;
789 $in_description = 0;
791 elsif ( /^DESCRIPTION$/ )
793 $in_description = 1;
795 elsif ($in_description == 1)
797 if ( !/^[A-Z]+$/ )
799 # Don't reject comments that refer to another doc (e.g. A/W)
800 if ( /^See ([A-Za-z0-9_]+)\.$/ )
802 if ($comment->{COMMENT_NAME} =~ /W$/ )
804 # This is probably a Unicode version of an Ascii function.
805 # Create the Ascii name and see if its been documented
806 my $ascii_name = $comment->{COMMENT_NAME};
807 $ascii_name =~ s/W$/A/;
809 my $ascii_export_index = $exported_names->{$ascii_name};
811 if (!defined($ascii_export_index))
813 $ascii_export_index = $implementation_names->{$ascii_name};
815 if (!defined($ascii_export_index))
817 if ($opt_verbose > 2)
819 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
822 else
824 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
825 if (@$ascii_export[4] & $FLAG_DOCUMENTED)
827 # Flag these functions as an A/W pair
828 @$ascii_export[4] |= $FLAG_APAIR;
829 @$export[4] |= $FLAG_WPAIR;
833 $found_returns = 1;
835 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
837 @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
838 $found_returns = 1;
840 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
842 @$export[4] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
843 $found_returns = 1;
845 $found_description_text = 1;
847 else
849 $in_description = 0;
853 if ($found_returns == 0 || $found_description_text == 0)
855 if ($opt_verbose > 2)
857 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
858 "description and/or RETURNS section, skipping\n";
860 $spec_details->{NUM_DOCS}--;
861 @$export[4] &= ~$FLAG_DOCUMENTED;
862 return;
866 process_comment_text($comment);
868 # Strip the prototypes return value, call convention, name and brackets
869 # (This leaves it as a list of types and names, or empty for void functions)
870 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
871 $prototype =~ s/ / /g;
873 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
875 $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
876 $comment->{RETURNS} = $1;
878 else
880 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\( *(.*)/$3/;
881 $comment->{RETURNS} = $1;
884 $prototype =~ s/ *\).*//; # Strip end bracket
885 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
886 $prototype =~ s/ *\, */\,/g; # Strip space around commas
887 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
888 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
889 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
891 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
893 # Find header file
894 my $h_file = "";
895 if (@$export[4] & $FLAG_NONAME)
897 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
899 else
901 if ($comment->{COMMENT_NAME} ne "")
903 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
904 $tmp = `$tmp`;
905 my $exit_value = $? >> 8;
906 if ($exit_value == 0)
908 $tmp =~ s/\n.*//g;
909 if ($tmp ne "")
911 $h_file = `basename $tmp`;
915 elsif ($comment->{ALT_NAME} ne "")
917 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
918 $tmp = `$tmp`;
919 my $exit_value = $? >> 8;
920 if ($exit_value == 0)
922 $tmp =~ s/\n.*//g;
923 if ($tmp ne "")
925 $h_file = `basename $tmp`;
929 $h_file =~ s/^ *//;
930 $h_file =~ s/\n//;
931 if ($h_file eq "")
933 $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine."
935 else
937 $h_file = "Defined in \"".$h_file."\".";
941 # Find source file
942 my $c_file = $comment->{FILE};
943 if ($opt_wine_root_dir ne "")
945 my $cfile = $pwd."/".$c_file; # Current dir + file
946 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
947 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
948 $cfile =~ s/\n//; # Strip newline
949 my $newfile = $c_file;
950 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
951 $cfile = $cfile."/".$newfile; # Append filename to base path
952 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
953 $cfile =~ s/\/\//\//g; # Remove any double slashes
954 $cfile =~ s/^\/+//; # Strip initial directory slash
955 $c_file = $cfile;
957 $c_file = "Implemented in \"".$c_file."\".";
959 # Add the implementation details
960 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
962 if (@$export[4] & $FLAG_I386)
964 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
966 if (@$export[4] & $FLAG_REGISTER)
968 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
969 "For more details, please read the source code.");
971 my $source_details = $source_files{$comment->{FILE}}[0];
972 if ($source_details->{DEBUG_CHANNEL} ne "")
974 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
977 # Write out the documentation for the API
978 output_comment($comment)
981 # process our extra comment and output it if it is suitable.
982 sub process_extra_comment
984 my $comment = shift(@_);
986 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
988 if (!defined($spec_details))
990 if ($opt_verbose > 2)
992 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
993 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
995 return;
998 # Check first to see if this is documentation for the DLL.
999 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
1001 if ($opt_verbose > 2)
1003 print "Info: Found DLL documentation\n";
1005 for (@{$comment->{TEXT}})
1007 push (@{$spec_details->{DESCRIPTION}}, $_);
1009 return;
1012 # Add the comment to the DLL page as a link
1013 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
1015 # If we have a prototype, process as a regular comment
1016 if (@{$comment->{PROTOTYPE}})
1018 $comment->{ORDINAL} = "@";
1020 # Add an index for the comment name
1021 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
1023 # Add a fake exported entry
1024 $spec_details->{NUM_EXPORTS}++;
1025 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
1026 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
1027 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
1028 push (@{$spec_details->{EXPORTS}},[@export]);
1029 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
1030 process_comment($comment);
1031 return;
1034 if ($opt_verbose > 0)
1036 print "Processing ",$comment->{COMMENT_NAME},"\n";
1039 if (@{$spec_details->{CURRENT_EXTRA}})
1041 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1043 if ($opt_verbose > 0)
1045 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1047 # Output the current comment
1048 process_comment_text($current_comment);
1049 output_open_api_file($current_comment->{COMMENT_NAME});
1050 output_api_header($current_comment);
1051 output_api_name($current_comment);
1052 output_api_comment($current_comment);
1053 output_api_footer($current_comment);
1054 output_close_api_file();
1057 if ($opt_verbose > 2)
1059 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1062 my $comment_copy =
1064 FILE => $comment->{FILE},
1065 COMMENT_NAME => $comment->{COMMENT_NAME},
1066 ALT_NAME => $comment->{ALT_NAME},
1067 DLL_NAME => $comment->{DLL_NAME},
1068 ORDINAL => $comment->{ORDINAL},
1069 RETURNS => $comment->{RETURNS},
1070 PROTOTYPE => [],
1071 TEXT => [],
1074 for (@{$comment->{TEXT}})
1076 push (@{$comment_copy->{TEXT}}, $_);
1078 # Set this comment to be the current extra comment
1079 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1082 # Write a standardised comment out in the appropriate format
1083 sub output_comment
1085 my $comment = shift(@_);
1087 if ($opt_verbose > 0)
1089 print "Processing ",$comment->{COMMENT_NAME},"\n";
1092 if ($opt_verbose > 4)
1094 print "--PROTO--\n";
1095 for (@{$comment->{PROTOTYPE}})
1097 print "'".$_."'\n";
1100 print "--COMMENT--\n";
1101 for (@{$comment->{TEXT} })
1103 print $_."\n";
1107 output_open_api_file($comment->{COMMENT_NAME});
1108 output_api_header($comment);
1109 output_api_name($comment);
1110 output_api_synopsis($comment);
1111 output_api_comment($comment);
1112 output_api_footer($comment);
1113 output_close_api_file();
1116 # Write out an index file for each .spec processed
1117 sub process_index_files
1119 foreach my $spec_file (keys %spec_files)
1121 my $spec_details = $spec_files{$spec_file}[0];
1122 if (defined ($spec_details->{DLL_NAME}))
1124 if (@{$spec_details->{CURRENT_EXTRA}})
1126 # We have an unwritten extra comment, write it
1127 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1128 process_extra_comment($current_comment);
1129 @{$spec_details->{CURRENT_EXTRA}} = ();
1131 output_spec($spec_details);
1136 # Write a spec files documentation out in the appropriate format
1137 sub output_spec
1139 my $spec_details = shift(@_);
1141 if ($opt_verbose > 2)
1143 print "Writing:",$spec_details->{DLL_NAME},"\n";
1146 # Use the comment output functions for consistency
1147 my $comment =
1149 FILE => $spec_details->{DLL_NAME},
1150 COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1151 ALT_NAME => $spec_details->{DLL_NAME},
1152 DLL_NAME => "",
1153 ORDINAL => "",
1154 RETURNS => "",
1155 PROTOTYPE => [],
1156 TEXT => [],
1158 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1159 $spec_details->{NUM_FUNCS};
1160 my $percent_implemented = 0;
1161 if ($total_implemented)
1163 $percent_implemented = $total_implemented /
1164 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1166 $percent_implemented = int($percent_implemented);
1167 my $percent_documented = 0;
1168 if ($spec_details->{NUM_DOCS})
1170 # Treat forwards and data as documented funcs for statistics
1171 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1172 $percent_documented = int($percent_documented);
1175 # Make a list of the contributors to this DLL. Do this only for the source
1176 # files that make up the DLL, because some directories specify multiple dlls.
1177 my @contributors;
1179 for (@{$spec_details->{SOURCES}})
1181 my $source_details = $source_files{$_}[0];
1182 for (@{$source_details->{CONTRIBUTORS}})
1184 push (@contributors, $_);
1188 my %saw;
1189 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1190 @contributors = sort @contributors;
1192 # Remove duplicates and blanks
1193 for(my $i=0; $i<@contributors; $i++)
1195 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1197 $contributors[$i-1] = $contributors[$i];
1200 undef %saw;
1201 @contributors = grep(!$saw{$_}++, @contributors);
1203 if ($opt_verbose > 3)
1205 print "Contributors:\n";
1206 for (@contributors)
1208 print "'".$_."'\n";
1211 my $contribstring = join (", ", @contributors);
1213 # Create the initial comment text
1214 @{$comment->{TEXT}} = (
1215 "NAME",
1216 $comment->{COMMENT_NAME}
1219 # Add the description, if we have one
1220 if (@{$spec_details->{DESCRIPTION}})
1222 push (@{$comment->{TEXT}}, "DESCRIPTION");
1223 for (@{$spec_details->{DESCRIPTION}})
1225 push (@{$comment->{TEXT}}, $_);
1229 # Add the statistics and contributors
1230 push (@{$comment->{TEXT}},
1231 "STATISTICS",
1232 "Forwards: ".$spec_details->{NUM_FORWARDS},
1233 "Variables: ".$spec_details->{NUM_VARS},
1234 "Stubs: ".$spec_details->{NUM_STUBS},
1235 "Functions: ".$spec_details->{NUM_FUNCS},
1236 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1237 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1238 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1239 "CONTRIBUTORS",
1240 "The following people hold copyrights on the source files comprising this dll:",
1242 $contribstring,
1243 "Note: This list may not be complete.",
1244 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1248 if ($opt_output_format eq "h")
1250 # Add the exports to the comment text
1251 push (@{$comment->{TEXT}},"EXPORTS");
1252 my $exports = $spec_details->{EXPORTS};
1253 for (@$exports)
1255 my $line = "";
1257 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1258 if (@$_[1] eq "forward")
1260 my $forward_dll = @$_[3];
1261 $forward_dll =~ s/\.(.*)//;
1262 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1264 elsif (@$_[1] eq "extern")
1266 $line = @$_[2]." (extern)";
1268 elsif (@$_[1] eq "stub")
1270 $line = @$_[2]." (stub)";
1272 elsif (@$_[1] eq "fake")
1274 # Don't add this function here, it gets listed with the extra documentation
1275 if (!(@$_[4] & $FLAG_WPAIR))
1277 # This function should be indexed
1278 push (@index_entries_list, @$_[3].",".@$_[3]);
1281 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1283 $line = @$_[2]." (data)";
1285 else
1287 # A function
1288 if (@$_[4] & $FLAG_DOCUMENTED)
1290 # Documented
1291 $line = @$_[2]." (implemented as ".@$_[3]."())";
1292 if (@$_[2] ne @$_[3])
1294 $line = @$_[2]." (implemented as ".@$_[3]."())";
1296 else
1298 $line = @$_[2]."()";
1300 if (!(@$_[4] & $FLAG_WPAIR))
1302 # This function should be indexed
1303 push (@index_entries_list, @$_[2].",".@$_[3]);
1306 else
1308 $line = @$_[2]." (not documented)";
1311 if ($line ne "")
1313 push (@{$comment->{TEXT}}, $line, "");
1317 # Add links to the extra documentation
1318 if (@{$spec_details->{EXTRA_COMMENTS}})
1320 push (@{$comment->{TEXT}}, "SEE ALSO");
1321 my %htmp;
1322 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1323 for (@{$spec_details->{EXTRA_COMMENTS}})
1325 push (@{$comment->{TEXT}}, $_."()", "");
1329 # The dll entry should also be indexed
1330 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1332 # Write out the document
1333 output_open_api_file($spec_details->{DLL_NAME});
1334 output_api_header($comment);
1335 output_api_comment($comment);
1336 output_api_footer($comment);
1337 output_close_api_file();
1339 # Add this dll to the database of dll names
1340 my $output_file = $opt_output_directory."/dlls.db";
1342 # Append the dllname to the output db of names
1343 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1344 print DLLDB $spec_details->{DLL_NAME},"\n";
1345 close(DLLDB);
1347 if ($opt_output_format eq "s")
1349 output_sgml_dll_file($spec_details);
1350 return;
1355 # OUTPUT FUNCTIONS
1356 # ----------------
1357 # Only these functions know anything about formatting for a specific
1358 # output type. The functions above work only with plain text.
1359 # This is to allow new types of output to be added easily.
1361 # Open the api file
1362 sub output_open_api_file
1364 my $output_name = shift(@_);
1365 $output_name = $opt_output_directory."/".$output_name;
1367 if ($opt_output_format eq "h")
1369 $output_name = $output_name.".html";
1371 elsif ($opt_output_format eq "s")
1373 $output_name = $output_name.".sgml";
1375 else
1377 $output_name = $output_name.".".$opt_manual_section;
1379 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1382 # Close the api file
1383 sub output_close_api_file
1385 close (OUTPUT);
1388 # Output the api file header
1389 sub output_api_header
1391 my $comment = shift(@_);
1393 if ($opt_output_format eq "h")
1395 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1396 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1397 print OUTPUT "<HTML>\n<HEAD>\n";
1398 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1399 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1400 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1401 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1403 elsif ($opt_output_format eq "s")
1405 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1406 "<sect1>\n",
1407 "<title>$comment->{COMMENT_NAME}</title>\n";
1409 else
1411 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1412 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1413 "Wine API\" \"Wine API\"\n";
1417 sub output_api_footer
1419 if ($opt_output_format eq "h")
1421 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1422 " All trademarks are the property of their respective owners.".
1423 " Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1424 " Generated $date.</i></p>\n</body>\n</html>\n";
1426 elsif ($opt_output_format eq "s")
1428 print OUTPUT "</sect1>\n";
1429 return;
1431 else
1436 sub output_api_section_start
1438 my $comment = shift(@_);
1439 my $section_name = shift(@_);
1441 if ($opt_output_format eq "h")
1443 print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1445 elsif ($opt_output_format eq "s")
1447 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1449 else
1451 print OUTPUT "\n\.SH ",$section_name,"\n";
1455 sub output_api_section_end
1457 # Not currently required by any output formats
1460 sub output_api_name
1462 my $comment = shift(@_);
1464 output_api_section_start($comment,"NAME");
1466 my $dll_ordinal = "";
1467 if ($comment->{ORDINAL} ne "")
1469 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1471 if ($opt_output_format eq "h")
1473 print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1474 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1475 ,$dll_ordinal,"</i></p>\n";
1477 elsif ($opt_output_format eq "s")
1479 print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>",
1480 $dll_ordinal,"</emphasis>\n</para>\n";
1482 else
1484 print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1487 output_api_section_end();
1490 sub output_api_synopsis
1492 my $comment = shift(@_);
1493 my @fmt;
1495 output_api_section_start($comment,"SYNOPSIS");
1497 if ($opt_output_format eq "h")
1499 print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1500 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1502 elsif ($opt_output_format eq "s")
1504 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1505 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1507 else
1509 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1510 @fmt = ("", "\n", "\\fI", "\\fR");
1513 # Since our prototype is output in a pre-formatted block, line up the
1514 # parameters and parameter comments in the same column.
1516 # First caluculate where the columns should start
1517 my $biggest_length = 0;
1518 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1520 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1521 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1523 my $length = length $1;
1524 if ($length > $biggest_length)
1526 $biggest_length = $length;
1531 # Now pad the string with blanks
1532 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1534 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1535 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1537 my $pad_len = $biggest_length - length $1;
1538 my $padding = " " x ($pad_len);
1539 ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1543 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1545 # Format the parameter name
1546 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1547 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1548 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1549 print OUTPUT $line;
1552 if ($opt_output_format eq "h")
1554 print OUTPUT " )\n\n</pre></p>\n";
1556 elsif ($opt_output_format eq "s")
1558 print OUTPUT " )\n</screen>\n";
1560 else
1562 print OUTPUT " )\n";
1565 output_api_section_end();
1568 sub output_api_comment
1570 my $comment = shift(@_);
1571 my $open_paragraph = 0;
1572 my $open_raw = 0;
1573 my $param_docs = 0;
1574 my @fmt;
1576 if ($opt_output_format eq "h")
1578 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1579 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1580 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1581 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1582 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1584 elsif ($opt_output_format eq "s")
1586 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1587 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1588 "<screen>\n","</screen>\n",
1589 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1590 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1591 "</entry>","</entry><entry>");
1593 else
1595 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1596 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1599 # Extract the parameter names
1600 my @parameter_names;
1601 for (@{$comment->{PROTOTYPE}})
1603 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1605 push (@parameter_names, $2);
1609 for (@{$comment->{TEXT}})
1611 if ($opt_output_format eq "h" || $opt_output_format eq "s")
1613 # Map special characters
1614 s/\&/\&amp;/g;
1615 s/\</\&lt;/g;
1616 s/\>/\&gt;/g;
1617 s/\([Cc]\)/\&copy;/g;
1618 s/\(tm\)/&#174;/;
1621 if ( s/^\|// )
1623 # Raw output
1624 if ($open_raw == 0)
1626 if ($open_paragraph == 1)
1628 # Close the open paragraph
1629 print OUTPUT $fmt[1];
1630 $open_paragraph = 0;
1632 # Start raw output
1633 print OUTPUT $fmt[12];
1634 $open_raw = 1;
1636 if ($opt_output_format eq "")
1638 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1640 print OUTPUT $_,"\n";
1642 else
1644 if ($opt_output_format eq "h")
1646 # Link to the file in WineHQ cvs
1647 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1649 # Highlight strings
1650 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1651 # Highlight literal chars
1652 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1653 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1654 # Highlight numeric constants
1655 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1657 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1658 # FIXME: Using bullet points for leading '-' would look nicer.
1659 if ($open_paragraph == 1)
1661 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1662 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1664 else
1666 s/^(\-)/$fmt[4]$1$fmt[5]/;
1667 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1670 if ($opt_output_format eq "h")
1672 # Html uses links for API calls
1673 s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1674 # Index references
1675 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1676 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1677 # And references to COM objects (hey, they'll get documented one day)
1678 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1679 # Convert any web addresses to real links
1680 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1682 else
1684 if ($opt_output_format eq "")
1686 # Give the man section for API calls
1687 s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1689 else
1691 # Highlight API calls
1692 s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1695 # And references to COM objects
1696 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1699 if ($open_raw == 1)
1701 # Finish the raw output
1702 print OUTPUT $fmt[13];
1703 $open_raw = 0;
1706 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1708 # Start of a new section
1709 if ($open_paragraph == 1)
1711 if ($param_docs == 1)
1713 print OUTPUT $fmt[17],$fmt[15];
1715 else
1717 print OUTPUT $fmt[1];
1719 $open_paragraph = 0;
1721 output_api_section_start($comment,$_);
1722 if ( /^PARAMS$/ )
1724 print OUTPUT $fmt[14];
1725 $param_docs = 1;
1727 else
1729 #print OUTPUT $fmt[15];
1730 $param_docs = 0;
1733 elsif ( /^$/ )
1735 # Empty line, indicating a new paragraph
1736 if ($open_paragraph == 1)
1738 if ($param_docs == 0)
1740 print OUTPUT $fmt[1];
1741 $open_paragraph = 0;
1745 else
1747 if ($param_docs == 1)
1749 if ($open_paragraph == 1)
1751 # For parameter docs, put each parameter into a new paragraph/table row
1752 print OUTPUT $fmt[17];
1753 $open_paragraph = 0;
1755 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1757 else
1759 # Within paragraph lines, prevent lines running together
1760 $_ = $_." ";
1763 # Format parameter names where they appear in the comment
1764 for my $parameter_name (@parameter_names)
1766 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\=\/])/$1$fmt[8]$2$fmt[9]$3/g;
1768 # Structure dereferences include the dereferenced member
1769 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1770 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1772 if ($open_paragraph == 0)
1774 if ($param_docs == 1)
1776 print OUTPUT $fmt[16];
1778 else
1780 print OUTPUT $fmt[0];
1782 $open_paragraph = 1;
1784 # Anything in all uppercase on its own gets emphasised
1785 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1787 print OUTPUT $_;
1791 if ($open_raw == 1)
1793 print OUTPUT $fmt[13];
1795 if ($open_paragraph == 1)
1797 print OUTPUT $fmt[1];
1801 # Create the master index file
1802 sub output_master_index_files
1804 if ($opt_output_format eq "")
1806 return; # No master index for man pages
1809 if ($opt_output_format eq "h")
1811 # Append the index entries to the output db of index entries
1812 my $output_file = $opt_output_directory."/index.db";
1813 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1814 for (@index_entries_list)
1816 $_ =~ s/A\,/\,/;
1817 print INDEXDB $_."\n";
1819 close(INDEXDB);
1822 # Use the comment output functions for consistency
1823 my $comment =
1825 FILE => "",
1826 COMMENT_NAME => "The Wine Api Guide",
1827 ALT_NAME => "The Wine Api Guide",
1828 DLL_NAME => "",
1829 ORDINAL => "",
1830 RETURNS => "",
1831 PROTOTYPE => [],
1832 TEXT => [],
1835 if ($opt_output_format eq "s")
1837 $comment->{COMMENT_NAME} = "Introduction";
1838 $comment->{ALT_NAME} = "Introduction",
1840 elsif ($opt_output_format eq "h")
1842 @{$comment->{TEXT}} = (
1843 "NAME",
1844 $comment->{COMMENT_NAME},
1845 "INTRODUCTION",
1849 # Create the initial comment text
1850 push (@{$comment->{TEXT}},
1851 "This document describes the Api calls made available",
1852 "by Wine. They are grouped by the dll that exports them.",
1854 "Please do not edit this document, since it is generated automatically",
1855 "from the Wine source code tree. Details on updating this documentation",
1856 "are given in the \"Wine Developers Guide\".",
1857 "CONTRIBUTORS",
1858 "Api documentation is generally written by the person who ",
1859 "implements a given Api call. Authors of each dll are listed in the overview ",
1860 "section for that dll. Additional contributors who have updated source files ",
1861 "but have not entered their names in a copyright statement are noted by an ",
1862 "entry in the file \"Changelog\" from the Wine source code distribution.",
1866 # Read in all dlls from the database of dll names
1867 my $input_file = $opt_output_directory."/dlls.db";
1868 my @dlls = `cat $input_file|sort|uniq`;
1870 if ($opt_output_format eq "h")
1872 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1873 push (@{$comment->{TEXT}},
1874 "INDEX",
1875 "For an alphabetical listing of the functions available, please click the ",
1876 "first letter of the functions name below:","",
1877 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1878 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1879 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1880 "DLLS",
1881 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1884 # Add the dlls to the comment
1885 for (@dlls)
1887 $_ =~ s/(\..*)?\n/\(\)/;
1888 push (@{$comment->{TEXT}}, $_, "");
1890 output_open_api_file("index");
1892 elsif ($opt_output_format eq "s")
1894 # Just write this as the initial blurb, with a chapter heading
1895 output_open_api_file("blurb");
1896 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1899 # Write out the document
1900 output_api_header($comment);
1901 output_api_comment($comment);
1902 output_api_footer($comment);
1903 if ($opt_output_format eq "s")
1905 print OUTPUT "</chapter>\n" # finish the chapter
1907 output_close_api_file();
1909 if ($opt_output_format eq "s")
1911 output_sgml_master_file(\@dlls);
1912 return;
1914 if ($opt_output_format eq "h")
1916 output_html_index_files();
1917 output_html_stylesheet();
1918 return;
1922 # Write the master wine-api.sgml, linking it to each dll.
1923 sub output_sgml_master_file
1925 my $dlls = shift(@_);
1927 output_open_api_file("wine-api");
1928 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1929 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1930 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1932 # List the entities
1933 for (@$dlls)
1935 $_ =~ s/(\..*)?\n//;
1936 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1939 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1940 print OUTPUT " &blurb;\n";
1942 for (@$dlls)
1944 print OUTPUT " &",$_,";\n"
1946 print OUTPUT "\n\n</book>\n";
1948 output_close_api_file();
1951 # Produce the sgml for the dll chapter from the generated files
1952 sub output_sgml_dll_file
1954 my $spec_details = shift(@_);
1956 # Make a list of all the documentation files to include
1957 my $exports = $spec_details->{EXPORTS};
1958 my @source_files = ();
1959 for (@$exports)
1961 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1962 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1963 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1965 # A documented function
1966 push (@source_files,@$_[3]);
1970 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1972 @source_files = sort @source_files;
1974 # create a new chapter for this dll
1975 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1976 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1977 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1978 output_close_api_file();
1980 # Add the sorted documentation, cleaning up as we go
1981 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1982 for (@source_files)
1984 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1985 `rm -f $opt_output_directory/$_.sgml`;
1988 # close the chapter, and overwite the dll source
1989 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1990 print OUTPUT "</chapter>\n";
1991 close OUTPUT;
1992 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1995 # Write the html index files containing the function names
1996 sub output_html_index_files
1998 if ($opt_output_format ne "h")
2000 return;
2003 my @letters = ('_', 'A' .. 'Z');
2005 # Read in all functions
2006 my $input_file = $opt_output_directory."/index.db";
2007 my @funcs = `cat $input_file|sort|uniq`;
2009 for (@letters)
2011 my $letter = $_;
2012 my $comment =
2014 FILE => "",
2015 COMMENT_NAME => "",
2016 ALT_NAME => "",
2017 DLL_NAME => "",
2018 ORDINAL => "",
2019 RETURNS => "",
2020 PROTOTYPE => [],
2021 TEXT => [],
2024 $comment->{COMMENT_NAME} = $letter." Functions";
2025 $comment->{ALT_NAME} = $letter." Functions";
2027 push (@{$comment->{TEXT}},
2028 "NAME",
2029 $comment->{COMMENT_NAME},
2030 "FUNCTIONS"
2033 # Add the functions to the comment
2034 for (@funcs)
2036 my $first_char = substr ($_, 0, 1);
2037 $first_char = uc $first_char;
2039 if ($first_char eq $letter)
2041 my $name = $_;
2042 my $file;
2043 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2044 $file = $2;
2045 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2049 # Write out the document
2050 output_open_api_file($letter);
2051 output_api_header($comment);
2052 output_api_comment($comment);
2053 output_api_footer($comment);
2054 output_close_api_file();
2058 # Output the stylesheet for HTML output
2059 sub output_html_stylesheet
2061 if ($opt_output_format ne "h")
2063 return;
2066 my $css;
2067 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2069 * Default styles for Wine HTML Documentation.
2071 * This style sheet should be altered to suit your needs/taste.
2073 BODY { /* Page body */
2074 background-color: white;
2075 color: black;
2076 font-family: Tahoma,sans-serif;
2077 font-style: normal;
2078 font-size: 10pt;
2080 a:link { color: #4444ff; } /* Links */
2081 a:visited { color: #333377 }
2082 a:active { color: #0000dd }
2083 H2.section { /* Section Headers */
2084 font-family: sans-serif;
2085 color: #777777;
2086 background-color: #F0F0FE;
2087 margin-left: 0.2in;
2088 margin-right: 1.0in;
2090 b.func_name { /* Function Name */
2091 font-size: 10pt;
2092 font-style: bold;
2094 i.dll_ord { /* Italicised DLL+ordinal */
2095 color: #888888;
2096 font-family: sans-serif;
2097 font-size: 8pt;
2099 p { /* Paragraphs */
2100 margin-left: 0.5in;
2101 margin-right: 0.5in;
2103 table { /* tables */
2104 margin-left: 0.5in;
2105 margin-right: 0.5in;
2107 pre.proto /* API Function prototype */
2109 border-style: solid;
2110 border-width: 1px;
2111 border-color: #777777;
2112 background-color: #F0F0BB;
2113 color: black;
2114 font-size: 10pt;
2115 vertical-align: top;
2116 margin-left: 0.5in;
2117 margin-right: 1.0in;
2119 pre.raw { /* Raw text output */
2120 margin-left: 0.6in;
2121 margin-right: 1.1in;
2122 background-color: #8080DC;
2124 tt.param { /* Parameter name */
2125 font-style: italic;
2126 color: blue;
2128 tt.const { /* Constant */
2129 color: red;
2131 i.in_out { /* In/Out */
2132 font-size: 8pt;
2133 color: grey;
2135 tt.coderef { /* Code in description text */
2136 color: darkgreen;
2138 b.emp /* Emphasis */ {
2139 font-style: bold;
2140 color: darkblue;
2142 i.footer { /* Footer */
2143 font-family: sans-serif;
2144 font-size: 6pt;
2145 color: darkgrey;
2147 HERE_TARGET
2149 my $output_file = "$opt_output_directory/apidoc.css";
2150 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2151 print CSS $css;
2152 close(CSS);