Tranlation of English README to Portuguese.
[wine/wine64.git] / tools / c2man.pl
blob3c78b9b5814ed619c35c77083697cefebd7c911c
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;
30 use bytes;
32 # Options
33 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
34 my $opt_manual_section = "3w";
35 my $opt_wine_root_dir = "";
36 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml
37 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
38 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
39 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
40 my @opt_header_file_list = ();
41 my @opt_spec_file_list = ();
42 my @opt_source_file_list = ();
44 # All the collected details about all the .spec files being processed
45 my %spec_files;
46 # All the collected details about all the source files being processed
47 my %source_files;
49 # useful globals
50 my $pwd = `pwd`."/";
51 $pwd =~ s/\n//;
52 my @datetime = localtime;
53 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
54 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
55 my $year = $datetime[5] + 1900;
56 my $date = "$months[$datetime[4]] $year";
58 sub usage
60 print "\nCreate API Documentation from Wine source code.\n\n",
61 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
62 "Where: <spec> is a .spec file giving a DLL's exports.\n",
63 " <include> is an include directory used by the DLL.\n",
64 " <source> is a source file of the DLL.\n",
65 " The above can be given multiple times on the command line, as appropriate.\n",
66 "Options:\n",
67 " -Th : Output HTML instead of a man page\n",
68 " -Ts : Output SGML (Docbook source) instead of a man page\n",
69 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
70 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
71 " -e : Output \"FIXME\" documentation from empty comments.\n",
72 " -v : Verbosity. Can be given more than once for more detail.\n";
75 # Print usage if we're called with no args
76 if( @ARGV == 0)
78 usage();
81 # Process command line options
82 while(defined($_ = shift @ARGV))
84 if( s/^-// )
86 # An option.
87 for ($_)
89 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
90 s/^S// && do { $opt_manual_section = $_; last; };
91 /^Th$/ && do { $opt_output_format = "h"; last; };
92 /^Ts$/ && do { $opt_output_format = "s"; last; };
93 /^v$/ && do { $opt_verbose++; last; };
94 /^e$/ && do { $opt_output_empty = 1; last; };
95 /^L$/ && do { last; };
96 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
97 s/^I// && do { my $include = $_."/*.h";
98 $include =~ s/\/\//\//g;
99 my $have_headers = `ls $include >/dev/null 2>&1`;
100 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
101 last;
103 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
104 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
105 $opt_wine_root_dir =~ s/\n//;
106 $opt_wine_root_dir =~ s/\/\//\//g;
107 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
108 last;
110 die "Unrecognised option $_\n";
113 else
115 # A source file.
116 push (@opt_source_file_list, $_);
120 # Remove duplicate include directories
121 my %htmp;
122 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
124 if ($opt_verbose > 3)
126 print "Output dir:'".$opt_output_directory."'\n";
127 print "Section :'".$opt_manual_section."'\n";
128 print "Format :'".$opt_output_format."'\n";
129 print "Root :'".$opt_wine_root_dir."'\n";
130 print "Spec files:'@opt_spec_file_list'\n";
131 print "Includes :'@opt_header_file_list'\n";
132 print "Sources :'@opt_source_file_list'\n";
135 if (@opt_spec_file_list == 0)
137 exit 0; # Don't bother processing non-dll files
140 # Read in each .spec files exports and other details
141 while(my $spec_file = shift @opt_spec_file_list)
143 process_spec_file($spec_file);
146 if ($opt_verbose > 3)
148 foreach my $spec_file ( keys %spec_files )
150 print "in '$spec_file':\n";
151 my $spec_details = $spec_files{$spec_file}[0];
152 my $exports = $spec_details->{EXPORTS};
153 for (@$exports)
155 print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n";
160 # Extract and output the comments from each source file
161 while(defined($_ = shift @opt_source_file_list))
163 process_source_file($_);
166 # Write the index files for each spec
167 process_index_files();
169 # Write the master index file
170 output_master_index_files();
172 exit 0;
175 # Generate the list of exported entries for the dll
176 sub process_spec_file
178 my $spec_name = shift(@_);
179 my $dll_name = $spec_name;
180 $dll_name =~ s/\..*//; # Strip the file extension
181 my $uc_dll_name = uc $dll_name;
183 my $spec_details =
185 NAME => $spec_name,
186 DLL_NAME => $dll_name,
187 NUM_EXPORTS => 0,
188 NUM_STUBS => 0,
189 NUM_FUNCS => 0,
190 NUM_FORWARDS => 0,
191 NUM_VARS => 0,
192 NUM_DOCS => 0,
193 CONTRIBUTORS => [ ],
194 SOURCES => [ ],
195 DESCRIPTION => [ ],
196 EXPORTS => [ ],
197 EXPORTED_NAMES => { },
198 IMPLEMENTATION_NAMES => { },
199 EXTRA_COMMENTS => [ ],
200 CURRENT_EXTRA => [ ] ,
203 if ($opt_verbose > 0)
205 print "Processing ".$spec_name."\n";
208 # We allow opening to fail just to cater for the peculiarities of
209 # the Wine build system. This doesn't hurt, in any case
210 open(SPEC_FILE, "<$spec_name") || return;
212 while(<SPEC_FILE>)
214 s/^\s+//; # Strip leading space
215 s/\s+\n$/\n/; # Strip trailing space
216 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
217 s/\s*#.*//; # Strip comments
218 s/\(.*\)/ /; # Strip arguments
219 s/ \-[a-z0-9]+//g; # Strip modifiers
220 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
221 s/\n$//; # Strip newline
223 if( /^(([0-9]+)|@) / )
225 # This line contains an exported symbol
226 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
228 for ($call_convention)
230 /^(cdecl|stdcall|varargs|pascal|pascal16)$/
231 && do { $spec_details->{NUM_FUNCS}++; last; };
232 /^(variable|equate)$/
233 && do { $spec_details->{NUM_VARS}++; last; };
234 /^(forward|extern)$/
235 && do { $spec_details->{NUM_FORWARDS}++; last; };
236 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
237 if ($opt_verbose > 0)
239 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
241 last;
244 # Convert ordinal only names so we can find them later
245 if ($exported_name eq "@")
247 $exported_name = $uc_dll_name.'_'.$ordinal;
249 if (!defined($implementation_name))
251 $implementation_name = $exported_name;
253 if ($implementation_name eq "")
255 $implementation_name = $exported_name;
257 # Add indices for the exported and implementation names
258 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
259 if ($implementation_name ne $exported_name)
261 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
264 # Add the exported entry
265 $spec_details->{NUM_EXPORTS}++;
266 my $documented = 0;
267 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
268 push (@{$spec_details->{EXPORTS}},[@export]);
271 close(SPEC_FILE);
273 # Add this .spec files details to the list of .spec files
274 $spec_files{$uc_dll_name} = [$spec_details];
277 # Read each source file, extract comments, and generate API documentation if appropriate
278 sub process_source_file
280 my $source_file = shift(@_);
281 my $source_details =
283 CONTRIBUTORS => [ ],
284 DEBUG_CHANNEL => "",
286 my $comment =
288 FILE => $source_file,
289 COMMENT_NAME => "",
290 ALT_NAME => "",
291 DLL_NAME => "",
292 ORDINAL => "",
293 RETURNS => "",
294 PROTOTYPE => [],
295 TEXT => [],
297 my $parse_state = 0;
298 my $ignore_blank_lines = 1;
299 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
301 if ($opt_verbose > 0)
303 print "Processing ".$source_file."\n";
305 open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n";
307 # Add this source file to the list of source files
308 $source_files{$source_file} = [$source_details];
310 while(<SOURCE_FILE>)
312 s/\n$//; # Strip newline
313 s/^\s+//; # Strip leading space
314 s/\s+$//; # Strip trailing space
315 if (! /^\*\|/ )
317 # Strip multiple tabs & spaces to a single space
318 s/\s+/ /g;
321 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
323 # Extract a contributor to this file
324 my $contributor = $2;
325 $contributor =~ s/ *$//;
326 $contributor =~ s/^by //;
327 $contributor =~ s/\.$//;
328 $contributor =~ s/ (for .*)/ \($1\)/;
329 if ($contributor ne "")
331 if ($opt_verbose > 3)
333 print "Info: Found contributor:'".$contributor."'\n";
335 push (@{$source_details->{CONTRIBUTORS}},$contributor);
338 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
340 # Extract the debug channel to use
341 if ($opt_verbose > 3)
343 print "Info: Found debug channel:'".$1."'\n";
345 $source_details->{DEBUG_CHANNEL} = $1;
348 if ($parse_state == 0) # Searching for a comment
350 if ( /^\/\**$/ )
352 # Found a comment start
353 $comment->{COMMENT_NAME} = "";
354 $comment->{ALT_NAME} = "";
355 $comment->{DLL_NAME} = "";
356 $comment->{ORDINAL} = "";
357 $comment->{RETURNS} = "";
358 $comment->{PROTOTYPE} = [];
359 $comment->{TEXT} = [];
360 $ignore_blank_lines = 1;
361 $extra_comment = 0;
362 $parse_state = 3;
365 elsif ($parse_state == 1) # Reading in a comment
367 if ( /^\**\// )
369 # Found the end of the comment
370 $parse_state = 2;
372 elsif ( s/^\*\|/\|/ )
374 # A line of comment not meant to be pre-processed
375 push (@{$comment->{TEXT}},$_); # Add the comment text
377 elsif ( s/^ *\** *// )
379 # A line of comment, starting with an asterisk
380 if ( /^[A-Z]+$/ || $_ eq "")
382 # This is a section start, so skip blank lines before and after it.
383 my $last_line = pop(@{$comment->{TEXT}});
384 if (defined($last_line) && $last_line ne "")
386 # Put it back
387 push (@{$comment->{TEXT}},$last_line);
389 if ( /^[A-Z]+$/ )
391 $ignore_blank_lines = 1;
393 else
395 $ignore_blank_lines = 0;
399 if ($ignore_blank_lines == 0 || $_ ne "")
401 push (@{$comment->{TEXT}},$_); # Add the comment text
404 else
406 # This isn't a well formatted comment: look for the next one
407 $parse_state = 0;
410 elsif ($parse_state == 2) # Finished reading in a comment
412 if ( /(WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
414 # Comment is followed by a function definition
415 $parse_state = 4; # Fall through to read prototype
417 else
419 # Allow cpp directives and blank lines between the comment and prototype
420 if ($extra_comment == 1)
422 # An extra comment not followed by a function definition
423 $parse_state = 5; # Fall through to process comment
425 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
427 # This isn't a well formatted comment: look for the next one
428 if ($opt_verbose > 1)
430 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
432 $parse_state = 0;
436 elsif ($parse_state == 3) # Reading in the first line of a comment
438 s/^ *\** *//;
439 if ( /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
441 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
442 $comment->{COMMENT_NAME} = $1;
443 $comment->{DLL_NAME} = uc $3;
444 $comment->{ORDINAL} = $4;
445 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
446 $parse_state = 1;
448 elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ )
450 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
451 $comment->{COMMENT_NAME} = $1;
452 $comment->{DLL_NAME} = uc $2;
453 $comment->{ORDINAL} = "";
454 $extra_comment = 1;
455 $parse_state = 1;
457 else
459 # This isn't a well formatted comment: look for the next one
460 $parse_state = 0;
464 if ($parse_state == 4) # Reading in the function definition
466 push (@{$comment->{PROTOTYPE}},$_);
467 # Strip comments from the line before checking for ')'
468 my $stripped_line = $_;
469 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
470 if ( $stripped_line =~ /\)/ )
472 # Strip a blank last line
473 my $last_line = pop(@{$comment->{TEXT}});
474 if (defined($last_line) && $last_line ne "")
476 # Put it back
477 push (@{$comment->{TEXT}},$last_line);
480 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
482 # Create a 'not implemented' comment
483 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
485 $parse_state = 5;
489 if ($parse_state == 5) # Processing the comment
491 # Process it, if it has any text
492 if (@{$comment->{TEXT}} > 0)
494 if ($extra_comment == 1)
496 process_extra_comment($comment);
498 else
500 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
501 process_comment($comment);
504 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
506 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
508 $parse_state = 0;
511 close(SOURCE_FILE);
514 # Standardise a comments text for consistency
515 sub process_comment_text
517 my $comment = shift(@_);
519 for (@{$comment->{TEXT}})
521 if (! /^\|/ )
523 # Map I/O values. These come in too many formats to standardise now....
524 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
525 s/\[O\]|\[o\]|\[out\]\[OUT\]/\[Out\]/g;
526 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
527 # TRUE/FALSE/NULL are defines, capitilise them
528 s/True|true/TRUE/g;
529 s/False|false/FALSE/g;
530 s/Null|null/NULL/g;
531 # Preferred capitalisations
532 s/ wine| WINE/ Wine/g;
533 s/ API | api / Api /g;
534 s/DLL| Dll /dll /g;
535 s/ URL | url / Url /g;
536 s/WIN16|win16/Win16/g;
537 s/WIN32|win32/Win32/g;
538 s/WIN64|win64/Win64/g;
539 s/ ID | id / Id /g;
540 # Grammar
541 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
542 s/ \:/\:/g; # Colons to the left
543 s/ \;/\;/g; # Semi-colons too
544 # Common idioms
545 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
546 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
547 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
548 # Trademarks
549 s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g;
550 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
551 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
552 s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
553 # Abbreviations
554 s/( chars)/characters/g;
555 s/( info )/ information /g;
556 s/( app )/ application /g;
557 s/( apps )/ applications /g;
562 # Standardise our comment and output it if it is suitable.
563 sub process_comment
565 my $comment = shift(@_);
567 # Don't process this comment if the function isnt exported
568 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
570 if (!defined($spec_details))
572 if ($opt_verbose > 2)
574 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
575 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
577 return;
580 if ($comment->{COMMENT_NAME} eq "@")
582 # Create an implementation name
583 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
586 my $exported_names = $spec_details->{EXPORTED_NAMES};
587 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
589 if (!defined($export_index))
591 # Perhaps the comment uses the implementation name?
592 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
593 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
595 if (!defined($export_index))
597 # This function doesn't appear to be exported. hmm.
598 if ($opt_verbose > 2)
600 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
601 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
603 return;
606 # When the function is exported twice we have the second name below the first
607 # (you see this a lot in ntdll, but also in some other places).
608 my $first_line = ${@{$comment->{TEXT}}}[1];
610 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
612 # Found a second name - mark it as documented
613 my $alt_index = $exported_names->{$1};
614 if (defined($alt_index))
616 if ($opt_verbose > 2)
618 print "Info: Found alternate name '",$1,"\n";
620 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
621 @$alt_export[4] |= 1;
622 $spec_details->{NUM_DOCS}++;
623 ${@{$comment->{TEXT}}}[1] = "";
627 if (@{$spec_details->{CURRENT_EXTRA}})
629 # We have an extra comment that might be related to this one
630 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
631 my $current_name = $current_comment->{COMMENT_NAME};
632 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
634 if ($opt_verbose > 2)
636 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
638 # Add a reference to this comment to our extra comment
639 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
643 # We want our docs generated using the implementation name, so they are unique
644 my $export = @{$spec_details->{EXPORTS}}[$export_index];
645 $comment->{COMMENT_NAME} = @$export[3];
646 $comment->{ALT_NAME} = @$export[2];
648 # Mark the function as documented
649 $spec_details->{NUM_DOCS}++;
650 @$export[4] |= 1;
652 # This file is used by the DLL - Make sure we get our contributors right
653 push (@{$spec_details->{SOURCES}},$comment->{FILE});
655 # If we have parameter comments in the prototype, extract them
656 my @parameter_comments;
657 for (@{$comment->{PROTOTYPE}})
659 s/ *\, */\,/g; # Strip spaces from around commas
661 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
663 my $parameter_comment = $2;
664 if (!$parameter_comment =~ /^\[/ )
666 # Add [IO] markers so we format the comment correctly
667 $parameter_comment = "[fixme] ".$parameter_comment;
669 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
671 # Add the parameter name
672 $parameter_comment = $2." ".$parameter_comment;
674 push (@parameter_comments, $parameter_comment);
678 # If we extracted any prototype comments, add them to the comment text.
679 if (@parameter_comments)
681 @parameter_comments = ("PARAMS", @parameter_comments);
682 my @new_comment = ();
683 my $inserted_params = 0;
685 for (@{$comment->{TEXT}})
687 if ( $inserted_params == 0 && /^[A-Z]+$/ )
689 # Found a section header, so this is where we insert
690 push (@new_comment, @parameter_comments);
691 $inserted_params = 1;
693 push (@new_comment, $_);
695 if ($inserted_params == 0)
697 # Add them to the end
698 push (@new_comment, @parameter_comments);
700 $comment->{TEXT} = [@new_comment];
703 if ($opt_fussy == 1 && $opt_output_empty == 0)
705 # Reject any comment that doesn't have a description or a RETURNS section.
706 # This is the default for now, 'coz many comments aren't suitable.
707 my $found_returns = 0;
708 my $found_description_text = 0;
709 my $in_description = 0;
710 for (@{$comment->{TEXT}})
712 if ( /^RETURNS$/ )
714 $found_returns = 1;
715 $in_description = 0;
717 elsif ( /^DESCRIPTION$/ )
719 $in_description = 1;
721 elsif ($in_description == 1)
723 if ( !/^[A-Z]+$/ )
725 if ( /^See ([A-Za-z0-9_]+)\.$/ || /^Unicode version of ([A-Za-z0-9_]+)\.$/)
727 # Dont reject comments that refer to their A/W couterpart
728 $found_returns = 1;
730 $found_description_text = 1;
732 else
734 $in_description = 0;
738 if ($found_returns == 0 || $found_description_text == 0)
740 if ($opt_verbose > 2)
742 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
743 "description and/or RETURNS section, skipping\n";
745 $spec_details->{NUM_DOCS}--;
746 @$export[4] &= ~1;
747 return;
751 process_comment_text($comment);
753 # Strip the prototypes return value, call convention, name and brackets
754 # (This leaves it as a list of types and names, or empty for void functions)
755 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
756 $prototype =~ s/ / /g;
757 $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
758 $comment->{RETURNS} = $1;
759 $prototype =~ s/ *\).*//; # Strip end bracket
760 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
761 $prototype =~ s/ *\, */\,/g; # Strip space around commas
762 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
763 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
764 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
766 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
768 # Find header file
769 my $h_file = "";
770 if ($comment->{COMMENT_NAME} ne "")
772 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
773 $tmp = `$tmp`;
774 my $exit_value = $? >> 8;
775 if ($exit_value == 0)
777 $tmp =~ s/\n.*//g;
778 if ($tmp ne "")
780 $h_file = `basename $tmp`;
784 elsif ($comment->{ALT_NAME} ne "")
786 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
787 $tmp = `$tmp`;
788 my $exit_value = $? >> 8;
789 if ($exit_value == 0)
791 $tmp =~ s/\n.*//g;
792 if ($tmp ne "")
794 $h_file = `basename $tmp`;
798 $h_file =~ s/^ *//;
799 $h_file =~ s/\n//;
800 if ($h_file eq "")
802 $h_file = "Not defined in a Wine header";
804 else
806 $h_file = "Defined in \"".$h_file."\"";
809 # Find source file
810 my $c_file = $comment->{FILE};
811 if ($opt_wine_root_dir ne "")
813 my $cfile = $pwd."/".$c_file; # Current dir + file
814 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
815 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
816 $cfile =~ s/\n//; # Strip newline
817 my $newfile = $c_file;
818 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
819 $cfile = $cfile."/".$newfile; # Append filename to base path
820 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
821 $cfile =~ s/\/\//\//g; # Remove any double slashes
822 $cfile =~ s/^\/+//; # Strip initial directory slash
823 $c_file = $cfile;
825 $c_file = "Implemented in \"".$c_file."\"";
827 # Add the implementation details
828 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
830 my $source_details = $source_files{$comment->{FILE}}[0];
831 if ($source_details->{DEBUG_CHANNEL} ne "")
833 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\"");
836 # Write out the documentation for the API
837 output_comment($comment)
840 # process our extra comment and output it if it is suitable.
841 sub process_extra_comment
843 my $comment = shift(@_);
845 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
847 if (!defined($spec_details))
849 if ($opt_verbose > 2)
851 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
852 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
854 return;
857 # Check first to see if this is documentation for the DLL.
858 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
860 if ($opt_verbose > 2)
862 print "Info: Found DLL documentation\n";
864 for (@{$comment->{TEXT}})
866 push (@{$spec_details->{DESCRIPTION}}, $_);
868 return;
871 # Add the comment to the DLL page as a link
872 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
874 # If we have a prototype, process as a regular comment
875 if (@{$comment->{PROTOTYPE}})
877 $comment->{ORDINAL} = "@";
879 # Add an index for the comment name
880 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
882 # Add a fake exported entry
883 $spec_details->{NUM_EXPORTS}++;
884 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
885 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
886 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
887 push (@{$spec_details->{EXPORTS}},[@export]);
888 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
889 process_comment($comment);
890 return;
893 if ($opt_verbose > 0)
895 print "Processing ",$comment->{COMMENT_NAME},"\n";
898 if (@{$spec_details->{CURRENT_EXTRA}})
900 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
902 if ($opt_verbose > 0)
904 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
906 # Output the current comment
907 process_comment_text($current_comment);
908 output_open_api_file($current_comment->{COMMENT_NAME});
909 output_api_header($current_comment);
910 output_api_name($current_comment);
911 output_api_comment($current_comment);
912 output_api_footer($current_comment);
913 output_close_api_file();
916 if ($opt_verbose > 2)
918 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
921 my $comment_copy =
923 FILE => $comment->{FILE},
924 COMMENT_NAME => $comment->{COMMENT_NAME},
925 ALT_NAME => $comment->{ALT_NAME},
926 DLL_NAME => $comment->{DLL_NAME},
927 ORDINAL => $comment->{ORDINAL},
928 RETURNS => $comment->{RETURNS},
929 PROTOTYPE => [],
930 TEXT => [],
933 for (@{$comment->{TEXT}})
935 push (@{$comment_copy->{TEXT}}, $_);
937 # Set this comment to be the current extra comment
938 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
941 # Write a standardised comment out in the appropriate format
942 sub output_comment
944 my $comment = shift(@_);
946 if ($opt_verbose > 0)
948 print "Processing ",$comment->{COMMENT_NAME},"\n";
951 if ($opt_verbose > 4)
953 print "--PROTO--\n";
954 for (@{$comment->{PROTOTYPE}})
956 print "'".$_."'\n";
959 print "--COMMENT--\n";
960 for (@{$comment->{TEXT} })
962 print $_."\n";
966 output_open_api_file($comment->{COMMENT_NAME});
967 output_api_header($comment);
968 output_api_name($comment);
969 output_api_synopsis($comment);
970 output_api_comment($comment);
971 output_api_footer($comment);
972 output_close_api_file();
975 # Write out an index file for each .spec processed
976 sub process_index_files
978 foreach my $spec_file (keys %spec_files)
980 my $spec_details = $spec_files{$spec_file}[0];
981 if (defined ($spec_details->{DLL_NAME}))
983 if (@{$spec_details->{CURRENT_EXTRA}})
985 # We have an unwritten extra comment, write it
986 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
987 process_extra_comment($current_comment);
988 @{$spec_details->{CURRENT_EXTRA}} = ();
990 output_spec($spec_details);
995 # Write a spec files documentation out in the appropriate format
996 sub output_spec
998 my $spec_details = shift(@_);
1000 if ($opt_verbose > 2)
1002 print "Writing:",$spec_details->{DLL_NAME},"\n";
1005 # Use the comment output functions for consistency
1006 my $comment =
1008 FILE => $spec_details->{DLL_NAME},
1009 COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1010 ALT_NAME => $spec_details->{DLL_NAME},
1011 DLL_NAME => "",
1012 ORDINAL => "",
1013 RETURNS => "",
1014 PROTOTYPE => [],
1015 TEXT => [],
1017 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1018 $spec_details->{NUM_FUNCS};
1019 my $percent_implemented = 0;
1020 if ($total_implemented)
1022 $percent_implemented = $total_implemented /
1023 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1025 $percent_implemented = int($percent_implemented);
1026 my $percent_documented = 0;
1027 if ($spec_details->{NUM_DOCS})
1029 # Treat forwards and data as documented funcs for statistics
1030 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1031 $percent_documented = int($percent_documented);
1034 # Make a list of the contributors to this DLL. Do this only for the source
1035 # files that make up the DLL, because some directories specify multiple dlls.
1036 my @contributors;
1038 for (@{$spec_details->{SOURCES}})
1040 my $source_details = $source_files{$_}[0];
1041 for (@{$source_details->{CONTRIBUTORS}})
1043 push (@contributors, $_);
1047 my %saw;
1048 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1049 @contributors = sort @contributors;
1051 # Remove duplicates and blanks
1052 for(my $i=0; $i<@contributors; $i++)
1054 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1056 $contributors[$i-1] = $contributors[$i];
1059 undef %saw;
1060 @contributors = grep(!$saw{$_}++, @contributors);
1062 if ($opt_verbose > 3)
1064 print "Contributors:\n";
1065 for (@contributors)
1067 print "'".$_."'\n";
1070 my $contribstring = join (", ", @contributors);
1072 # Create the initial comment text
1073 @{$comment->{TEXT}} = (
1074 "NAME",
1075 $comment->{COMMENT_NAME}
1078 # Add the description, if we have one
1079 if (@{$spec_details->{DESCRIPTION}})
1081 push (@{$comment->{TEXT}}, "DESCRIPTION");
1082 for (@{$spec_details->{DESCRIPTION}})
1084 push (@{$comment->{TEXT}}, $_);
1088 # Add the statistics and contributors
1089 push (@{$comment->{TEXT}},
1090 "STATISTICS",
1091 "Forwards: ".$spec_details->{NUM_FORWARDS},
1092 "Variables: ".$spec_details->{NUM_VARS},
1093 "Stubs: ".$spec_details->{NUM_STUBS},
1094 "Functions: ".$spec_details->{NUM_FUNCS},
1095 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1096 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1097 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1098 "CONTRIBUTORS",
1099 "The following people hold copyrights on the source files comprising this dll:",
1101 $contribstring,
1102 "Note: This list may not be complete.",
1103 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1107 if ($opt_output_format eq "h")
1109 # Add the exports to the comment text
1110 push (@{$comment->{TEXT}},"EXPORTS");
1111 my $exports = $spec_details->{EXPORTS};
1112 for (@$exports)
1114 my $line = "";
1116 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1117 if (@$_[1] eq "forward")
1119 my $forward_dll = @$_[3];
1120 $forward_dll =~ s/\.(.*)//;
1121 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1123 elsif (@$_[1] eq "extern")
1125 $line = @$_[2]." (extern)";
1127 elsif (@$_[1] eq "stub")
1129 $line = @$_[2]." (stub)";
1131 elsif (@$_[1] eq "fake")
1133 # Don't add this function here, it gets listed with the extra documentation
1135 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1137 $line = @$_[2]." (data)";
1139 else
1141 # A function
1142 if (@$_[4] & 1)
1144 # Documented
1145 $line = @$_[2]." (implemented as ".@$_[3]."())";
1146 if (@$_[2] ne @$_[3])
1148 $line = @$_[2]." (implemented as ".@$_[3]."())";
1150 else
1152 $line = @$_[2]."()";
1155 else
1157 $line = @$_[2]." (not documented)";
1160 if ($line ne "")
1162 push (@{$comment->{TEXT}}, $line, "");
1166 # Add links to the extra documentation
1167 if (@{$spec_details->{EXTRA_COMMENTS}})
1169 push (@{$comment->{TEXT}}, "SEE ALSO");
1170 my %htmp;
1171 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1172 for (@{$spec_details->{EXTRA_COMMENTS}})
1174 push (@{$comment->{TEXT}}, $_."()", "");
1178 # Write out the document
1179 output_open_api_file($spec_details->{DLL_NAME});
1180 output_api_header($comment);
1181 output_api_comment($comment);
1182 output_api_footer($comment);
1183 output_close_api_file();
1185 # Add this dll to the database of dll names
1186 my $output_file = $opt_output_directory."/dlls.db";
1188 # Append the dllname to the output db of names
1189 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1190 print DLLDB $spec_details->{DLL_NAME},"\n";
1191 close(DLLDB);
1193 if ($opt_output_format eq "s")
1195 output_sgml_dll_file($spec_details);
1196 return;
1201 # OUTPUT FUNCTIONS
1202 # ----------------
1203 # Only these functions know anything about formatting for a specific
1204 # output type. The functions above work only with plain text.
1205 # This is to allow new types of output to be added easily.
1207 # Open the api file
1208 sub output_open_api_file
1210 my $output_name = shift(@_);
1211 $output_name = $opt_output_directory."/".$output_name;
1213 if ($opt_output_format eq "h")
1215 $output_name = $output_name.".html";
1217 elsif ($opt_output_format eq "s")
1219 $output_name = $output_name.".sgml";
1221 else
1223 $output_name = $output_name.".".$opt_manual_section;
1225 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1228 # Close the api file
1229 sub output_close_api_file
1231 close (OUTPUT);
1234 # Output the api file header
1235 sub output_api_header
1237 my $comment = shift(@_);
1239 if ($opt_output_format eq "h")
1241 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1242 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1243 print OUTPUT "<HTML>\n<HEAD>\n";
1244 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1245 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1246 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1247 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1249 elsif ($opt_output_format eq "s")
1251 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1252 "<sect1>\n",
1253 "<title>$comment->{COMMENT_NAME}</title>\n";
1255 else
1257 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1258 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1259 "Wine API\" \"Wine API\"\n";
1263 sub output_api_footer
1265 if ($opt_output_format eq "h")
1267 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1268 "Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1269 "Generated $date</i></p>\n</body>\n</html>\n";
1271 elsif ($opt_output_format eq "s")
1273 print OUTPUT "</sect1>\n";
1274 return;
1276 else
1281 sub output_api_section_start
1283 my $comment = shift(@_);
1284 my $section_name = shift(@_);
1286 if ($opt_output_format eq "h")
1288 print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1290 elsif ($opt_output_format eq "s")
1292 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1294 else
1296 print OUTPUT "\n\.SH ",$section_name,"\n";
1300 sub output_api_section_end
1302 # Not currently required by any output formats
1305 sub output_api_name
1307 my $comment = shift(@_);
1309 output_api_section_start($comment,"NAME");
1311 my $dll_ordinal = "";
1312 if ($comment->{ORDINAL} ne "")
1314 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1316 if ($opt_output_format eq "h")
1318 print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1319 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1320 ,$dll_ordinal,"</i></p>\n";
1322 elsif ($opt_output_format eq "s")
1324 print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>",
1325 $dll_ordinal,"</emphasis>\n</para>\n";
1327 else
1329 print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1332 output_api_section_end();
1335 sub output_api_synopsis
1337 my $comment = shift(@_);
1338 my @fmt;
1340 output_api_section_start($comment,"SYNOPSIS");
1342 if ($opt_output_format eq "h")
1344 print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1345 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1347 elsif ($opt_output_format eq "s")
1349 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1350 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1352 else
1354 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1355 @fmt = ("", "\n", "\\fI", "\\fR");
1358 # Since our prototype is output in a pre-formatted block, line up the
1359 # parameters and parameter comments in the same column.
1361 # First caluculate where the columns should start
1362 my $biggest_length = 0;
1363 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1365 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1366 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1368 my $length = length $1;
1369 if ($length > $biggest_length)
1371 $biggest_length = $length;
1376 # Now pad the string with blanks
1377 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1379 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1380 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1382 my $pad_len = $biggest_length - length $1;
1383 my $padding = " " x ($pad_len);
1384 ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1388 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1390 # Format the parameter name
1391 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1392 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1393 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1394 print OUTPUT $line;
1397 if ($opt_output_format eq "h")
1399 print OUTPUT " )\n\n</pre></p>\n";
1401 elsif ($opt_output_format eq "s")
1403 print OUTPUT " )\n</screen>\n";
1405 else
1407 print OUTPUT " )\n";
1410 output_api_section_end();
1413 sub output_api_comment
1415 my $comment = shift(@_);
1416 my $open_paragraph = 0;
1417 my $open_raw = 0;
1418 my $param_docs = 0;
1419 my @fmt;
1421 if ($opt_output_format eq "h")
1423 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1424 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1425 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1426 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1427 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1429 elsif ($opt_output_format eq "s")
1431 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1432 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1433 "<screen>\n","</screen>\n",
1434 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1435 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1436 "</entry>","</entry><entry>");
1438 else
1440 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1441 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1444 # Extract the parameter names
1445 my @parameter_names;
1446 for (@{$comment->{PROTOTYPE}})
1448 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1450 push (@parameter_names, $2);
1454 for (@{$comment->{TEXT}})
1456 if ($opt_output_format eq "h" || $opt_output_format eq "s")
1458 # Map special characters
1459 s/\&/\&amp;/g;
1460 s/\</\&lt;/g;
1461 s/\>/\&gt;/g;
1462 s/\([Cc]\)/\&copy;/g;
1463 s/\(tm\)/&#174;/;
1466 if ( s/^\|// )
1468 # Raw output
1469 if ($open_raw == 0)
1471 if ($open_paragraph == 1)
1473 # Close the open paragraph
1474 print OUTPUT $fmt[1];
1475 $open_paragraph = 0;
1477 # Start raw output
1478 print OUTPUT $fmt[12];
1479 $open_raw = 1;
1481 if ($opt_output_format eq "")
1483 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1485 print OUTPUT $_,"\n";
1487 else
1489 # Highlight strings
1490 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1491 # Highlight literal chars
1492 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1493 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1494 # Highlight numeric constants
1495 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1497 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1498 # FIXME: Using bullet points for leading '-' would look nicer.
1499 if ($open_paragraph == 1)
1501 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1502 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1504 else
1506 s/^(\-)/$fmt[4]$1$fmt[5]/;
1507 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1510 if ($opt_output_format eq "h")
1512 # Html uses links for API calls
1513 s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1514 # And references to COM objects (hey, they'll get documented one day)
1515 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1516 # Convert any web addresses to real links
1517 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1519 else
1521 if ($opt_output_format eq "")
1523 # Give the man section for API calls
1524 s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1526 else
1528 # Highlight API calls
1529 s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1532 # And references to COM objects
1533 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1536 if ($open_raw == 1)
1538 # Finish the raw output
1539 print OUTPUT $fmt[13];
1540 $open_raw = 0;
1543 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1545 # Start of a new section
1546 if ($open_paragraph == 1)
1548 if ($param_docs == 1)
1550 print OUTPUT $fmt[17],$fmt[15];
1552 else
1554 print OUTPUT $fmt[1];
1556 $open_paragraph = 0;
1558 output_api_section_start($comment,$_);
1559 if ( /^PARAMS$/ )
1561 print OUTPUT $fmt[14];
1562 $param_docs = 1;
1564 else
1566 #print OUTPUT $fmt[15];
1567 $param_docs = 0;
1570 elsif ( /^$/ )
1572 # Empty line, indicating a new paragraph
1573 if ($open_paragraph == 1)
1575 if ($param_docs == 0)
1577 print OUTPUT $fmt[1];
1578 $open_paragraph = 0;
1582 else
1584 if ($param_docs == 1)
1586 if ($open_paragraph == 1)
1588 # For parameter docs, put each parameter into a new paragraph/table row
1589 print OUTPUT $fmt[17];
1590 $open_paragraph = 0;
1592 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19]/; # Format In/Out
1594 else
1596 # Within paragraph lines, prevent lines running together
1597 $_ = $_." ";
1600 # Format parameter names where they appear in the comment
1601 for my $parameter_name (@parameter_names)
1603 s/(^|[ \.\,\(\-])($parameter_name)($|[ \.\)\,\-])/$1$fmt[8]$2$fmt[9]$3/g;
1606 if ($open_paragraph == 0)
1608 if ($param_docs == 1)
1610 print OUTPUT $fmt[16];
1612 else
1614 print OUTPUT $fmt[0];
1616 $open_paragraph = 1;
1618 # Anything in all uppercase on its own gets emphasised
1619 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1621 print OUTPUT $_;
1625 if ($open_raw == 1)
1627 print OUTPUT $fmt[13];
1629 if ($open_paragraph == 1)
1631 print OUTPUT $fmt[1];
1635 # Create the master index file
1636 sub output_master_index_files
1638 if ($opt_output_format eq "")
1640 return; # No master index for man pages
1643 # Use the comment output functions for consistency
1644 my $comment =
1646 FILE => "",
1647 COMMENT_NAME => "The Wine Api Guide",
1648 ALT_NAME => "The Wine Api Guide",
1649 DLL_NAME => "",
1650 ORDINAL => "",
1651 RETURNS => "",
1652 PROTOTYPE => [],
1653 TEXT => [],
1656 if ($opt_output_format eq "s")
1658 $comment->{COMMENT_NAME} = "Introduction";
1659 $comment->{ALT_NAME} = "Introduction",
1661 elsif ($opt_output_format eq "h")
1663 @{$comment->{TEXT}} = (
1664 "NAME",
1665 $comment->{COMMENT_NAME},
1666 "INTRODUCTION",
1670 # Create the initial comment text
1671 push (@{$comment->{TEXT}},
1672 "This document describes the Api calls made available",
1673 "by Wine. They are grouped by the dll that exports them.",
1675 "Please do not edit this document, since it is generated automatically",
1676 "from the Wine source code tree. Details on updating this documentation",
1677 "are given in the \"Wine Developers Guide\".",
1678 "CONTRIBUTORS",
1679 "Api documentation is generally written by the person who ",
1680 "implements a given Api call. Authors of each dll are listed in the overview ",
1681 "section for that dll. Additional contributors who have updated source files ",
1682 "but have not entered their names in a copyright statement are noted by an ",
1683 "entry in the file \"Changelog\" from the Wine source code distribution.",
1687 # Read in all dlls from the database of dll names
1688 my $input_file = $opt_output_directory."/dlls.db";
1689 my @dlls = `cat $input_file|sort|uniq`;
1691 if ($opt_output_format eq "h")
1693 # HTML gets a list of all the dlls. For docbook the index creates this for us
1694 push (@{$comment->{TEXT}},
1695 "DLLS",
1696 "The following dlls are provided by Wine:",
1699 # Add the dlls to the comment
1700 for (@dlls)
1702 $_ =~ s/(\..*)?\n/\(\)/;
1703 push (@{$comment->{TEXT}}, $_, "");
1705 output_open_api_file("index");
1707 elsif ($opt_output_format eq "s")
1709 # Just write this as the initial blurb, with a chapter heading
1710 output_open_api_file("blurb");
1711 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1714 # Write out the document
1715 output_api_header($comment);
1716 output_api_comment($comment);
1717 output_api_footer($comment);
1718 if ($opt_output_format eq "s")
1720 print OUTPUT "</chapter>\n" # finish the chapter
1722 output_close_api_file();
1724 if ($opt_output_format eq "s")
1726 output_sgml_master_file(\@dlls);
1727 return;
1729 if ($opt_output_format eq "h")
1731 output_html_stylesheet();
1732 # FIXME: Create an alphabetical index
1733 return;
1737 # Write the master wine-api.sgml, linking it to each dll.
1738 sub output_sgml_master_file
1740 my $dlls = shift(@_);
1742 output_open_api_file("wine-api");
1743 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1744 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1745 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1747 # List the entities
1748 for (@$dlls)
1750 $_ =~ s/(\..*)?\n//;
1751 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1754 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1755 print OUTPUT " &blurb;\n";
1757 for (@$dlls)
1759 print OUTPUT " &",$_,";\n"
1761 print OUTPUT "\n\n</book>\n";
1763 output_close_api_file();
1766 # Produce the sgml for the dll chapter from the generated files
1767 sub output_sgml_dll_file
1769 my $spec_details = shift(@_);
1771 # Make a list of all the documentation files to include
1772 my $exports = $spec_details->{EXPORTS};
1773 my @source_files = ();
1774 for (@$exports)
1776 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1777 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1778 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1780 # A documented function
1781 push (@source_files,@$_[3]);
1785 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1787 @source_files = sort @source_files;
1789 # create a new chapter for this dll
1790 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1791 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1792 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1793 output_close_api_file();
1795 # Add the sorted documentation, cleaning up as we go
1796 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1797 for (@source_files)
1799 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1800 `rm -f $opt_output_directory/$_.sgml`;
1803 # close the chapter, and overwite the dll source
1804 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1805 print OUTPUT "</chapter>\n";
1806 close OUTPUT;
1807 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1810 # Output the stylesheet for HTML output
1811 sub output_html_stylesheet
1813 if ($opt_output_format ne "h")
1815 return;
1818 my $css;
1819 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
1821 * Default styles for Wine HTML Documentation.
1823 * This style sheet should be altered to suit your needs/taste.
1825 BODY { /* Page body */
1826 background-color: white;
1827 color: black;
1828 font-family: Tahomsans-serif;
1829 font-style: normal;
1830 font-size: 10pt;
1832 a:link { color: #4444ff; } /* Links */
1833 a:visited { color: #333377 }
1834 a:active { color: #0000dd }
1835 H2.section { /* Section Headers */
1836 font-family: sans-serif;
1837 color: #777777;
1838 background-color: #F0F0FE;
1839 margin-left: 0.2in;
1840 margin-right: 1.0in;
1842 b.func_name { /* Function Name */
1843 font-size: 10pt;
1844 font-style: bold;
1846 i.dll_ord { /* Italicised DLL+ordinal */
1847 color: #888888;
1848 font-family: sans-serif;
1849 font-size: 8pt;
1851 p { /* Paragraphs */
1852 margin-left: 0.5in;
1853 margin-right: 0.5in;
1855 table { /* tables */
1856 margin-left: 0.5in;
1857 margin-right: 0.5in;
1859 pre.proto /* API Function prototype */
1861 border-style: solid;
1862 border-width: 1px;
1863 border-color: #777777;
1864 background-color: #F0F0BB;
1865 color: black;
1866 font-size: 10pt;
1867 vertical-align: top;
1868 margin-left: 0.5in;
1869 margin-right: 1.0in;
1871 tt.param { /* Parameter name */
1872 font-style: italic;
1873 color: blue;
1875 tt.const { /* Constant */
1876 color: red;
1878 i.in_out { /* In/Out */
1879 font-size: 8pt;
1880 color: grey;
1882 tt.coderef { /* Code in description text */
1883 color: darkgreen;
1885 b.emp /* Emphasis */ {
1886 font-style: bold;
1887 color: darkblue;
1889 i.footer { /* Footer */
1890 font-family: sans-serif;
1891 font-size: 6pt;
1892 color: darkgrey;
1894 HERE_TARGET
1896 my $output_file = "$opt_output_directory/apidoc.css";
1897 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
1898 print CSS $css;
1899 close(CSS);