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