- moved into new mscvpdb.h (out of msc.c) all types and defines needed
[wine/multimedia.git] / tools / c2man.pl
blobb4e24cc1d70b81ab271aa9006a3829008a4a3e23
1 #! /usr/bin/perl -w
3 # Generate API documentation. See documentation/documentation.sgml for details.
5 # Copyright (C) 2000 Mike McCormack
6 # Copyright (C) 2003 Jon Griffiths
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 # TODO
23 # SGML gurus - feel free to smarten up the SGML.
24 # Add any other relevant information for the dll - imports etc
25 # Should we have a special output mode for WineHQ?
27 use strict;
28 use bytes;
30 # Function flags. most of these come from the spec flags
31 my $FLAG_DOCUMENTED = 1;
32 my $FLAG_NONAME = 2;
33 my $FLAG_I386 = 4;
34 my $FLAG_REGISTER = 8;
35 my $FLAG_APAIR = 16; # The A version of a matching W function
36 my $FLAG_WPAIR = 32; # The W version of a matching A function
37 my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function
40 # Options
41 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
42 my $opt_manual_section = "3w";
43 my $opt_wine_root_dir = "";
44 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml
45 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
46 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
47 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
48 my @opt_header_file_list = ();
49 my @opt_spec_file_list = ();
50 my @opt_source_file_list = ();
52 # All the collected details about all the .spec files being processed
53 my %spec_files;
54 # All the collected details about all the source files being processed
55 my %source_files;
56 # All documented functions that are to be placed in the index
57 my @index_entries_list = ();
59 # useful globals
60 my $pwd = `pwd`."/";
61 $pwd =~ s/\n//;
62 my @datetime = localtime;
63 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
64 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
65 my $year = $datetime[5] + 1900;
66 my $date = "$months[$datetime[4]] $year";
69 sub output_api_comment($);
70 sub output_api_footer($);
71 sub output_api_header($);
72 sub output_api_name($);
73 sub output_api_synopsis($);
74 sub output_close_api_file();
75 sub output_comment($);
76 sub output_html_index_files();
77 sub output_html_stylesheet();
78 sub output_open_api_file($);
79 sub output_sgml_dll_file($);
80 sub output_sgml_master_file($);
81 sub output_spec($);
82 sub process_comment($);
83 sub process_extra_comment($);
86 # Generate the list of exported entries for the dll
87 sub process_spec_file($)
89 my $spec_name = shift;
90 my $dll_name = $spec_name;
91 $dll_name =~ s/\..*//; # Strip the file extension
92 my $uc_dll_name = uc $dll_name;
94 my $spec_details =
96 NAME => $spec_name,
97 DLL_NAME => $dll_name,
98 NUM_EXPORTS => 0,
99 NUM_STUBS => 0,
100 NUM_FUNCS => 0,
101 NUM_FORWARDS => 0,
102 NUM_VARS => 0,
103 NUM_DOCS => 0,
104 CONTRIBUTORS => [ ],
105 SOURCES => [ ],
106 DESCRIPTION => [ ],
107 EXPORTS => [ ],
108 EXPORTED_NAMES => { },
109 IMPLEMENTATION_NAMES => { },
110 EXTRA_COMMENTS => [ ],
111 CURRENT_EXTRA => [ ] ,
114 if ($opt_verbose > 0)
116 print "Processing ".$spec_name."\n";
119 # We allow opening to fail just to cater for the peculiarities of
120 # the Wine build system. This doesn't hurt, in any case
121 open(SPEC_FILE, "<$spec_name") || return;
123 while(<SPEC_FILE>)
125 s/^\s+//; # Strip leading space
126 s/\s+\n$/\n/; # Strip trailing space
127 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
128 s/\s*#.*//; # Strip comments
129 s/\(.*\)/ /; # Strip arguments
130 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
131 s/\n$//; # Strip newline
133 my $flags = 0;
134 if( /\-noname/ )
136 $flags |= $FLAG_NONAME;
138 if( /\-i386/ )
140 $flags |= $FLAG_I386;
142 if( /\-register/ )
144 $flags |= $FLAG_REGISTER;
146 s/ \-[a-z0-9]+//g; # Strip flags
148 if( /^(([0-9]+)|@) / )
150 # This line contains an exported symbol
151 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
153 for ($call_convention)
155 /^(cdecl|stdcall|varargs|pascal)$/
156 && do { $spec_details->{NUM_FUNCS}++; last; };
157 /^(variable|equate)$/
158 && do { $spec_details->{NUM_VARS}++; last; };
159 /^(extern)$/
160 && do { $spec_details->{NUM_FORWARDS}++; last; };
161 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
162 if ($opt_verbose > 0)
164 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
166 last;
169 # Convert ordinal only names so we can find them later
170 if ($exported_name eq "@")
172 $exported_name = $uc_dll_name.'_'.$ordinal;
174 if (!defined($implementation_name))
176 $implementation_name = $exported_name;
178 if ($implementation_name eq "")
180 $implementation_name = $exported_name;
183 if ($implementation_name =~ /(.*?)\./)
185 $call_convention = "forward"; # Referencing a function from another dll
186 $spec_details->{NUM_FUNCS}--;
187 $spec_details->{NUM_FORWARDS}++;
190 # Add indices for the exported and implementation names
191 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
192 if ($implementation_name ne $exported_name)
194 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
197 # Add the exported entry
198 $spec_details->{NUM_EXPORTS}++;
199 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
200 push (@{$spec_details->{EXPORTS}},[@export]);
203 close(SPEC_FILE);
205 # Add this .spec files details to the list of .spec files
206 $spec_files{$uc_dll_name} = [$spec_details];
209 # Read each source file, extract comments, and generate API documentation if appropriate
210 sub process_source_file($)
212 my $source_file = shift;
213 my $source_details =
215 CONTRIBUTORS => [ ],
216 DEBUG_CHANNEL => "",
218 my $comment =
220 FILE => $source_file,
221 COMMENT_NAME => "",
222 ALT_NAME => "",
223 DLL_NAME => "",
224 ORDINAL => "",
225 RETURNS => "",
226 PROTOTYPE => [],
227 TEXT => [],
229 my $parse_state = 0;
230 my $ignore_blank_lines = 1;
231 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
233 if ($opt_verbose > 0)
235 print "Processing ".$source_file."\n";
237 open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n";
239 # Add this source file to the list of source files
240 $source_files{$source_file} = [$source_details];
242 while(<SOURCE_FILE>)
244 s/\n$//; # Strip newline
245 s/^\s+//; # Strip leading space
246 s/\s+$//; # Strip trailing space
247 if (! /^\*\|/ )
249 # Strip multiple tabs & spaces to a single space
250 s/\s+/ /g;
253 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
255 # Extract a contributor to this file
256 my $contributor = $2;
257 $contributor =~ s/ *$//;
258 $contributor =~ s/^by //;
259 $contributor =~ s/\.$//;
260 $contributor =~ s/ (for .*)/ \($1\)/;
261 if ($contributor ne "")
263 if ($opt_verbose > 3)
265 print "Info: Found contributor:'".$contributor."'\n";
267 push (@{$source_details->{CONTRIBUTORS}},$contributor);
270 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
272 # Extract the debug channel to use
273 if ($opt_verbose > 3)
275 print "Info: Found debug channel:'".$1."'\n";
277 $source_details->{DEBUG_CHANNEL} = $1;
280 if ($parse_state == 0) # Searching for a comment
282 if ( /^\/\**$/ )
284 # Found a comment start
285 $comment->{COMMENT_NAME} = "";
286 $comment->{ALT_NAME} = "";
287 $comment->{DLL_NAME} = "";
288 $comment->{ORDINAL} = "";
289 $comment->{RETURNS} = "";
290 $comment->{PROTOTYPE} = [];
291 $comment->{TEXT} = [];
292 $ignore_blank_lines = 1;
293 $extra_comment = 0;
294 $parse_state = 3;
297 elsif ($parse_state == 1) # Reading in a comment
299 if ( /^\**\// )
301 # Found the end of the comment
302 $parse_state = 2;
304 elsif ( s/^\*\|/\|/ )
306 # A line of comment not meant to be pre-processed
307 push (@{$comment->{TEXT}},$_); # Add the comment text
309 elsif ( s/^ *\** *// )
311 # A line of comment, starting with an asterisk
312 if ( /^[A-Z]+$/ || $_ eq "")
314 # This is a section start, so skip blank lines before and after it.
315 my $last_line = pop(@{$comment->{TEXT}});
316 if (defined($last_line) && $last_line ne "")
318 # Put it back
319 push (@{$comment->{TEXT}},$last_line);
321 if ( /^[A-Z]+$/ )
323 $ignore_blank_lines = 1;
325 else
327 $ignore_blank_lines = 0;
331 if ($ignore_blank_lines == 0 || $_ ne "")
333 push (@{$comment->{TEXT}},$_); # Add the comment text
336 else
338 # This isn't a well formatted comment: look for the next one
339 $parse_state = 0;
342 elsif ($parse_state == 2) # Finished reading in a comment
344 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
345 /.*?\(/ )
347 # Comment is followed by a function definition
348 $parse_state = 4; # Fall through to read prototype
350 else
352 # Allow cpp directives and blank lines between the comment and prototype
353 if ($extra_comment == 1)
355 # An extra comment not followed by a function definition
356 $parse_state = 5; # Fall through to process comment
358 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
360 # This isn't a well formatted comment: look for the next one
361 if ($opt_verbose > 1)
363 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
365 $parse_state = 0;
369 elsif ($parse_state == 3) # Reading in the first line of a comment
371 s/^ *\** *//;
372 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
374 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
375 $comment->{COMMENT_NAME} = $1;
376 $comment->{DLL_NAME} = uc $3;
377 $comment->{ORDINAL} = $4;
378 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
379 $parse_state = 1;
381 elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ )
383 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
384 $comment->{COMMENT_NAME} = $1;
385 $comment->{DLL_NAME} = uc $2;
386 $comment->{ORDINAL} = "";
387 $extra_comment = 1;
388 $parse_state = 1;
390 else
392 # This isn't a well formatted comment: look for the next one
393 $parse_state = 0;
397 if ($parse_state == 4) # Reading in the function definition
399 push (@{$comment->{PROTOTYPE}},$_);
400 # Strip comments from the line before checking for ')'
401 my $stripped_line = $_;
402 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
403 if ( $stripped_line =~ /\)/ )
405 # Strip a blank last line
406 my $last_line = pop(@{$comment->{TEXT}});
407 if (defined($last_line) && $last_line ne "")
409 # Put it back
410 push (@{$comment->{TEXT}},$last_line);
413 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
415 # Create a 'not implemented' comment
416 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
418 $parse_state = 5;
422 if ($parse_state == 5) # Processing the comment
424 # Process it, if it has any text
425 if (@{$comment->{TEXT}} > 0)
427 if ($extra_comment == 1)
429 process_extra_comment($comment);
431 else
433 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
434 process_comment($comment);
437 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
439 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
441 $parse_state = 0;
444 close(SOURCE_FILE);
447 # Standardise a comments text for consistency
448 sub process_comment_text($)
450 my $comment = shift;
451 my $i = 0;
453 for (@{$comment->{TEXT}})
455 if (! /^\|/ )
457 # Map I/O values. These come in too many formats to standardise now....
458 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
459 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
460 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
461 # TRUE/FALSE/NULL are defines, capitilise them
462 s/True|true/TRUE/g;
463 s/False|false/FALSE/g;
464 s/Null|null/NULL/g;
465 # Preferred capitalisations
466 s/ wine| WINE/ Wine/g;
467 s/ API | api / Api /g;
468 s/DLL|Dll/dll /g;
469 s/ URL | url / Url /g;
470 s/WIN16|win16/Win16/g;
471 s/WIN32|win32/Win32/g;
472 s/WIN64|win64/Win64/g;
473 s/ ID | id / Id /g;
474 # Grammar
475 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
476 s/ \:/\:/g; # Colons to the left
477 s/ \;/\;/g; # Semi-colons too
478 # Common idioms
479 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
480 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
481 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
482 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
483 # Trademarks
484 s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g;
485 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
486 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
487 s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
488 # Abbreviations
489 s/( char )/ character /g;
490 s/( chars )/ characters /g;
491 s/( info )/ information /g;
492 s/( app )/ application /g;
493 s/( apps )/ applications /g;
494 s/( exe )/ executable /g;
495 s/( ptr )/ pointer /g;
496 s/( obj )/ object /g;
497 s/( err )/ error /g;
498 s/( bool )/ boolean /g;
499 # Punctuation
500 if ( /\[I|\[O/ && ! /\.$/ )
502 $_ = $_."."; # Always have a full stop at the end of parameter desc.
504 elsif ($i > 0 && /^[A-Z]*$/ &&
505 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
506 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
509 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
511 # Paragraphs always end with a full stop
512 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
516 $i++;
520 # Standardise our comment and output it if it is suitable.
521 sub process_comment($)
523 my $comment = shift;
525 # Don't process this comment if the function isn't exported
526 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
528 if (!defined($spec_details))
530 if ($opt_verbose > 2)
532 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
533 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
535 return;
538 if ($comment->{COMMENT_NAME} eq "@")
540 my $found = 0;
542 # Find the name from the .spec file
543 for (@{$spec_details->{EXPORTS}})
545 if (@$_[0] eq $comment->{ORDINAL})
547 $comment->{COMMENT_NAME} = @$_[2];
548 $found = 1;
552 if ($found == 0)
554 # Create an implementation name
555 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
559 my $exported_names = $spec_details->{EXPORTED_NAMES};
560 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
561 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
563 if (!defined($export_index))
565 # Perhaps the comment uses the implementation name?
566 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
568 if (!defined($export_index))
570 # This function doesn't appear to be exported. hmm.
571 if ($opt_verbose > 2)
573 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
574 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
576 return;
579 # When the function is exported twice we have the second name below the first
580 # (you see this a lot in ntdll, but also in some other places).
581 my $first_line = ${@{$comment->{TEXT}}}[1];
583 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
585 # Found a second name - mark it as documented
586 my $alt_index = $exported_names->{$1};
587 if (defined($alt_index))
589 if ($opt_verbose > 2)
591 print "Info: Found alternate name '",$1,"\n";
593 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
594 @$alt_export[4] |= $FLAG_DOCUMENTED;
595 $spec_details->{NUM_DOCS}++;
596 ${@{$comment->{TEXT}}}[1] = "";
600 if (@{$spec_details->{CURRENT_EXTRA}})
602 # We have an extra comment that might be related to this one
603 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
604 my $current_name = $current_comment->{COMMENT_NAME};
605 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
607 if ($opt_verbose > 2)
609 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
611 # Add a reference to this comment to our extra comment
612 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
616 # We want our docs generated using the implementation name, so they are unique
617 my $export = @{$spec_details->{EXPORTS}}[$export_index];
618 $comment->{COMMENT_NAME} = @$export[3];
619 $comment->{ALT_NAME} = @$export[2];
621 # Mark the function as documented
622 $spec_details->{NUM_DOCS}++;
623 @$export[4] |= $FLAG_DOCUMENTED;
625 # This file is used by the DLL - Make sure we get our contributors right
626 push (@{$spec_details->{SOURCES}},$comment->{FILE});
628 # If we have parameter comments in the prototype, extract them
629 my @parameter_comments;
630 for (@{$comment->{PROTOTYPE}})
632 s/ *\, */\,/g; # Strip spaces from around commas
634 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
636 my $parameter_comment = $2;
637 if (!$parameter_comment =~ /^\[/ )
639 # Add [IO] markers so we format the comment correctly
640 $parameter_comment = "[fixme] ".$parameter_comment;
642 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
644 # Add the parameter name
645 $parameter_comment = $2." ".$parameter_comment;
647 push (@parameter_comments, $parameter_comment);
651 # If we extracted any prototype comments, add them to the comment text.
652 if (@parameter_comments)
654 @parameter_comments = ("PARAMS", @parameter_comments);
655 my @new_comment = ();
656 my $inserted_params = 0;
658 for (@{$comment->{TEXT}})
660 if ( $inserted_params == 0 && /^[A-Z]+$/ )
662 # Found a section header, so this is where we insert
663 push (@new_comment, @parameter_comments);
664 $inserted_params = 1;
666 push (@new_comment, $_);
668 if ($inserted_params == 0)
670 # Add them to the end
671 push (@new_comment, @parameter_comments);
673 $comment->{TEXT} = [@new_comment];
676 if ($opt_fussy == 1 && $opt_output_empty == 0)
678 # Reject any comment that doesn't have a description or a RETURNS section.
679 # This is the default for now, 'coz many comments aren't suitable.
680 my $found_returns = 0;
681 my $found_description_text = 0;
682 my $in_description = 0;
683 for (@{$comment->{TEXT}})
685 if ( /^RETURNS$/ )
687 $found_returns = 1;
688 $in_description = 0;
690 elsif ( /^DESCRIPTION$/ )
692 $in_description = 1;
694 elsif ($in_description == 1)
696 if ( !/^[A-Z]+$/ )
698 # Don't reject comments that refer to another doc (e.g. A/W)
699 if ( /^See ([A-Za-z0-9_]+)\.$/ )
701 if ($comment->{COMMENT_NAME} =~ /W$/ )
703 # This is probably a Unicode version of an Ascii function.
704 # Create the Ascii name and see if its been documented
705 my $ascii_name = $comment->{COMMENT_NAME};
706 $ascii_name =~ s/W$/A/;
708 my $ascii_export_index = $exported_names->{$ascii_name};
710 if (!defined($ascii_export_index))
712 $ascii_export_index = $implementation_names->{$ascii_name};
714 if (!defined($ascii_export_index))
716 if ($opt_verbose > 2)
718 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
721 else
723 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
724 if (@$ascii_export[4] & $FLAG_DOCUMENTED)
726 # Flag these functions as an A/W pair
727 @$ascii_export[4] |= $FLAG_APAIR;
728 @$export[4] |= $FLAG_WPAIR;
732 $found_returns = 1;
734 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
736 @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
737 $found_returns = 1;
739 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
741 @$export[4] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
742 $found_returns = 1;
744 $found_description_text = 1;
746 else
748 $in_description = 0;
752 if ($found_returns == 0 || $found_description_text == 0)
754 if ($opt_verbose > 2)
756 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
757 "description and/or RETURNS section, skipping\n";
759 $spec_details->{NUM_DOCS}--;
760 @$export[4] &= ~$FLAG_DOCUMENTED;
761 return;
765 process_comment_text($comment);
767 # Strip the prototypes return value, call convention, name and brackets
768 # (This leaves it as a list of types and names, or empty for void functions)
769 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
770 $prototype =~ s/ / /g;
772 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
774 $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
775 $comment->{RETURNS} = $1;
777 else
779 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\( *(.*)/$3/;
780 $comment->{RETURNS} = $1;
783 $prototype =~ s/ *\).*//; # Strip end bracket
784 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
785 $prototype =~ s/ *\, */\,/g; # Strip space around commas
786 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
787 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
788 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
790 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
792 # Find header file
793 my $h_file = "";
794 if (@$export[4] & $FLAG_NONAME)
796 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
798 else
800 if ($comment->{COMMENT_NAME} ne "")
802 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
803 $tmp = `$tmp`;
804 my $exit_value = $? >> 8;
805 if ($exit_value == 0)
807 $tmp =~ s/\n.*//g;
808 if ($tmp ne "")
810 $h_file = `basename $tmp`;
814 elsif ($comment->{ALT_NAME} ne "")
816 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
817 $tmp = `$tmp`;
818 my $exit_value = $? >> 8;
819 if ($exit_value == 0)
821 $tmp =~ s/\n.*//g;
822 if ($tmp ne "")
824 $h_file = `basename $tmp`;
828 $h_file =~ s/^ *//;
829 $h_file =~ s/\n//;
830 if ($h_file eq "")
832 $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine."
834 else
836 $h_file = "Defined in \"".$h_file."\".";
840 # Find source file
841 my $c_file = $comment->{FILE};
842 if ($opt_wine_root_dir ne "")
844 my $cfile = $pwd."/".$c_file; # Current dir + file
845 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
846 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
847 $cfile =~ s/\n//; # Strip newline
848 my $newfile = $c_file;
849 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
850 $cfile = $cfile."/".$newfile; # Append filename to base path
851 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
852 $cfile =~ s/\/\//\//g; # Remove any double slashes
853 $cfile =~ s/^\/+//; # Strip initial directory slash
854 $c_file = $cfile;
856 $c_file = "Implemented in \"".$c_file."\".";
858 # Add the implementation details
859 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
861 if (@$export[4] & $FLAG_I386)
863 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
865 if (@$export[4] & $FLAG_REGISTER)
867 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
868 "For more details, please read the source code.");
870 my $source_details = $source_files{$comment->{FILE}}[0];
871 if ($source_details->{DEBUG_CHANNEL} ne "")
873 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
876 # Write out the documentation for the API
877 output_comment($comment)
880 # process our extra comment and output it if it is suitable.
881 sub process_extra_comment($)
883 my $comment = shift;
885 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
887 if (!defined($spec_details))
889 if ($opt_verbose > 2)
891 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
892 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
894 return;
897 # Check first to see if this is documentation for the DLL.
898 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
900 if ($opt_verbose > 2)
902 print "Info: Found DLL documentation\n";
904 for (@{$comment->{TEXT}})
906 push (@{$spec_details->{DESCRIPTION}}, $_);
908 return;
911 # Add the comment to the DLL page as a link
912 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
914 # If we have a prototype, process as a regular comment
915 if (@{$comment->{PROTOTYPE}})
917 $comment->{ORDINAL} = "@";
919 # Add an index for the comment name
920 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
922 # Add a fake exported entry
923 $spec_details->{NUM_EXPORTS}++;
924 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
925 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
926 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
927 push (@{$spec_details->{EXPORTS}},[@export]);
928 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
929 process_comment($comment);
930 return;
933 if ($opt_verbose > 0)
935 print "Processing ",$comment->{COMMENT_NAME},"\n";
938 if (@{$spec_details->{CURRENT_EXTRA}})
940 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
942 if ($opt_verbose > 0)
944 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
946 # Output the current comment
947 process_comment_text($current_comment);
948 output_open_api_file($current_comment->{COMMENT_NAME});
949 output_api_header($current_comment);
950 output_api_name($current_comment);
951 output_api_comment($current_comment);
952 output_api_footer($current_comment);
953 output_close_api_file();
956 if ($opt_verbose > 2)
958 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
961 my $comment_copy =
963 FILE => $comment->{FILE},
964 COMMENT_NAME => $comment->{COMMENT_NAME},
965 ALT_NAME => $comment->{ALT_NAME},
966 DLL_NAME => $comment->{DLL_NAME},
967 ORDINAL => $comment->{ORDINAL},
968 RETURNS => $comment->{RETURNS},
969 PROTOTYPE => [],
970 TEXT => [],
973 for (@{$comment->{TEXT}})
975 push (@{$comment_copy->{TEXT}}, $_);
977 # Set this comment to be the current extra comment
978 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
981 # Write a standardised comment out in the appropriate format
982 sub output_comment($)
984 my $comment = shift;
986 if ($opt_verbose > 0)
988 print "Processing ",$comment->{COMMENT_NAME},"\n";
991 if ($opt_verbose > 4)
993 print "--PROTO--\n";
994 for (@{$comment->{PROTOTYPE}})
996 print "'".$_."'\n";
999 print "--COMMENT--\n";
1000 for (@{$comment->{TEXT} })
1002 print $_."\n";
1006 output_open_api_file($comment->{COMMENT_NAME});
1007 output_api_header($comment);
1008 output_api_name($comment);
1009 output_api_synopsis($comment);
1010 output_api_comment($comment);
1011 output_api_footer($comment);
1012 output_close_api_file();
1015 # Write out an index file for each .spec processed
1016 sub process_index_files()
1018 foreach my $spec_file (keys %spec_files)
1020 my $spec_details = $spec_files{$spec_file}[0];
1021 if (defined ($spec_details->{DLL_NAME}))
1023 if (@{$spec_details->{CURRENT_EXTRA}})
1025 # We have an unwritten extra comment, write it
1026 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1027 process_extra_comment($current_comment);
1028 @{$spec_details->{CURRENT_EXTRA}} = ();
1030 output_spec($spec_details);
1035 # Write a spec files documentation out in the appropriate format
1036 sub output_spec($)
1038 my $spec_details = shift;
1040 if ($opt_verbose > 2)
1042 print "Writing:",$spec_details->{DLL_NAME},"\n";
1045 # Use the comment output functions for consistency
1046 my $comment =
1048 FILE => $spec_details->{DLL_NAME},
1049 COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1050 ALT_NAME => $spec_details->{DLL_NAME},
1051 DLL_NAME => "",
1052 ORDINAL => "",
1053 RETURNS => "",
1054 PROTOTYPE => [],
1055 TEXT => [],
1057 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1058 $spec_details->{NUM_FUNCS};
1059 my $percent_implemented = 0;
1060 if ($total_implemented)
1062 $percent_implemented = $total_implemented /
1063 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1065 $percent_implemented = int($percent_implemented);
1066 my $percent_documented = 0;
1067 if ($spec_details->{NUM_DOCS})
1069 # Treat forwards and data as documented funcs for statistics
1070 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1071 $percent_documented = int($percent_documented);
1074 # Make a list of the contributors to this DLL. Do this only for the source
1075 # files that make up the DLL, because some directories specify multiple dlls.
1076 my @contributors;
1078 for (@{$spec_details->{SOURCES}})
1080 my $source_details = $source_files{$_}[0];
1081 for (@{$source_details->{CONTRIBUTORS}})
1083 push (@contributors, $_);
1087 my %saw;
1088 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1089 @contributors = sort @contributors;
1091 # Remove duplicates and blanks
1092 for(my $i=0; $i<@contributors; $i++)
1094 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1096 $contributors[$i-1] = $contributors[$i];
1099 undef %saw;
1100 @contributors = grep(!$saw{$_}++, @contributors);
1102 if ($opt_verbose > 3)
1104 print "Contributors:\n";
1105 for (@contributors)
1107 print "'".$_."'\n";
1110 my $contribstring = join (", ", @contributors);
1112 # Create the initial comment text
1113 @{$comment->{TEXT}} = (
1114 "NAME",
1115 $comment->{COMMENT_NAME}
1118 # Add the description, if we have one
1119 if (@{$spec_details->{DESCRIPTION}})
1121 push (@{$comment->{TEXT}}, "DESCRIPTION");
1122 for (@{$spec_details->{DESCRIPTION}})
1124 push (@{$comment->{TEXT}}, $_);
1128 # Add the statistics and contributors
1129 push (@{$comment->{TEXT}},
1130 "STATISTICS",
1131 "Forwards: ".$spec_details->{NUM_FORWARDS},
1132 "Variables: ".$spec_details->{NUM_VARS},
1133 "Stubs: ".$spec_details->{NUM_STUBS},
1134 "Functions: ".$spec_details->{NUM_FUNCS},
1135 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1136 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1137 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1138 "CONTRIBUTORS",
1139 "The following people hold copyrights on the source files comprising this dll:",
1141 $contribstring,
1142 "Note: This list may not be complete.",
1143 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1147 if ($opt_output_format eq "h")
1149 # Add the exports to the comment text
1150 push (@{$comment->{TEXT}},"EXPORTS");
1151 my $exports = $spec_details->{EXPORTS};
1152 for (@$exports)
1154 my $line = "";
1156 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1157 if (@$_[1] eq "forward")
1159 my $forward_dll = @$_[3];
1160 $forward_dll =~ s/\.(.*)//;
1161 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1163 elsif (@$_[1] eq "extern")
1165 $line = @$_[2]." (extern)";
1167 elsif (@$_[1] eq "stub")
1169 $line = @$_[2]." (stub)";
1171 elsif (@$_[1] eq "fake")
1173 # Don't add this function here, it gets listed with the extra documentation
1174 if (!(@$_[4] & $FLAG_WPAIR))
1176 # This function should be indexed
1177 push (@index_entries_list, @$_[3].",".@$_[3]);
1180 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1182 $line = @$_[2]." (data)";
1184 else
1186 # A function
1187 if (@$_[4] & $FLAG_DOCUMENTED)
1189 # Documented
1190 $line = @$_[2]." (implemented as ".@$_[3]."())";
1191 if (@$_[2] ne @$_[3])
1193 $line = @$_[2]." (implemented as ".@$_[3]."())";
1195 else
1197 $line = @$_[2]."()";
1199 if (!(@$_[4] & $FLAG_WPAIR))
1201 # This function should be indexed
1202 push (@index_entries_list, @$_[2].",".@$_[3]);
1205 else
1207 $line = @$_[2]." (not documented)";
1210 if ($line ne "")
1212 push (@{$comment->{TEXT}}, $line, "");
1216 # Add links to the extra documentation
1217 if (@{$spec_details->{EXTRA_COMMENTS}})
1219 push (@{$comment->{TEXT}}, "SEE ALSO");
1220 my %htmp;
1221 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1222 for (@{$spec_details->{EXTRA_COMMENTS}})
1224 push (@{$comment->{TEXT}}, $_."()", "");
1228 # The dll entry should also be indexed
1229 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1231 # Write out the document
1232 output_open_api_file($spec_details->{DLL_NAME});
1233 output_api_header($comment);
1234 output_api_comment($comment);
1235 output_api_footer($comment);
1236 output_close_api_file();
1238 # Add this dll to the database of dll names
1239 my $output_file = $opt_output_directory."/dlls.db";
1241 # Append the dllname to the output db of names
1242 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1243 print DLLDB $spec_details->{DLL_NAME},"\n";
1244 close(DLLDB);
1246 if ($opt_output_format eq "s")
1248 output_sgml_dll_file($spec_details);
1249 return;
1254 # OUTPUT FUNCTIONS
1255 # ----------------
1256 # Only these functions know anything about formatting for a specific
1257 # output type. The functions above work only with plain text.
1258 # This is to allow new types of output to be added easily.
1260 # Open the api file
1261 sub output_open_api_file($)
1263 my $output_name = shift;
1264 $output_name = $opt_output_directory."/".$output_name;
1266 if ($opt_output_format eq "h")
1268 $output_name = $output_name.".html";
1270 elsif ($opt_output_format eq "s")
1272 $output_name = $output_name.".sgml";
1274 else
1276 $output_name = $output_name.".".$opt_manual_section;
1278 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1281 # Close the api file
1282 sub output_close_api_file()
1284 close (OUTPUT);
1287 # Output the api file header
1288 sub output_api_header($)
1290 my $comment = shift;
1292 if ($opt_output_format eq "h")
1294 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1295 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1296 print OUTPUT "<HTML>\n<HEAD>\n";
1297 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1298 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1299 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1300 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1302 elsif ($opt_output_format eq "s")
1304 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1305 "<sect1>\n",
1306 "<title>$comment->{COMMENT_NAME}</title>\n";
1308 else
1310 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1311 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1312 "Wine API\" \"Wine API\"\n";
1316 sub output_api_footer($)
1318 if ($opt_output_format eq "h")
1320 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1321 " All trademarks are the property of their respective owners.".
1322 " Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1323 " Generated $date.</i></p>\n</body>\n</html>\n";
1325 elsif ($opt_output_format eq "s")
1327 print OUTPUT "</sect1>\n";
1328 return;
1330 else
1335 sub output_api_section_start($$)
1337 my $comment = shift;
1338 my $section_name = shift;
1340 if ($opt_output_format eq "h")
1342 print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1344 elsif ($opt_output_format eq "s")
1346 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1348 else
1350 print OUTPUT "\n\.SH ",$section_name,"\n";
1354 sub output_api_section_end()
1356 # Not currently required by any output formats
1359 sub output_api_name($)
1361 my $comment = shift;
1363 output_api_section_start($comment,"NAME");
1365 my $dll_ordinal = "";
1366 if ($comment->{ORDINAL} ne "")
1368 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1370 if ($opt_output_format eq "h")
1372 print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1373 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1374 ,$dll_ordinal,"</i></p>\n";
1376 elsif ($opt_output_format eq "s")
1378 print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>",
1379 $dll_ordinal,"</emphasis>\n</para>\n";
1381 else
1383 print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1386 output_api_section_end();
1389 sub output_api_synopsis($)
1391 my $comment = shift;
1392 my @fmt;
1394 output_api_section_start($comment,"SYNOPSIS");
1396 if ($opt_output_format eq "h")
1398 print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1399 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1401 elsif ($opt_output_format eq "s")
1403 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1404 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1406 else
1408 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1409 @fmt = ("", "\n", "\\fI", "\\fR");
1412 # Since our prototype is output in a pre-formatted block, line up the
1413 # parameters and parameter comments in the same column.
1415 # First caluculate where the columns should start
1416 my $biggest_length = 0;
1417 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1419 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1420 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1422 my $length = length $1;
1423 if ($length > $biggest_length)
1425 $biggest_length = $length;
1430 # Now pad the string with blanks
1431 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1433 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1434 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1436 my $pad_len = $biggest_length - length $1;
1437 my $padding = " " x ($pad_len);
1438 ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1442 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1444 # Format the parameter name
1445 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1446 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1447 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1448 print OUTPUT $line;
1451 if ($opt_output_format eq "h")
1453 print OUTPUT " )\n\n</pre></p>\n";
1455 elsif ($opt_output_format eq "s")
1457 print OUTPUT " )\n</screen>\n";
1459 else
1461 print OUTPUT " )\n";
1464 output_api_section_end();
1467 sub output_api_comment($)
1469 my $comment = shift;
1470 my $open_paragraph = 0;
1471 my $open_raw = 0;
1472 my $param_docs = 0;
1473 my @fmt;
1475 if ($opt_output_format eq "h")
1477 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1478 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1479 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1480 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1481 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1483 elsif ($opt_output_format eq "s")
1485 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1486 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1487 "<screen>\n","</screen>\n",
1488 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1489 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1490 "</entry>","</entry><entry>");
1492 else
1494 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1495 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1498 # Extract the parameter names
1499 my @parameter_names;
1500 for (@{$comment->{PROTOTYPE}})
1502 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1504 push (@parameter_names, $2);
1508 for (@{$comment->{TEXT}})
1510 if ($opt_output_format eq "h" || $opt_output_format eq "s")
1512 # Map special characters
1513 s/\&/\&amp;/g;
1514 s/\</\&lt;/g;
1515 s/\>/\&gt;/g;
1516 s/\([Cc]\)/\&copy;/g;
1517 s/\(tm\)/&#174;/;
1520 if ( s/^\|// )
1522 # Raw output
1523 if ($open_raw == 0)
1525 if ($open_paragraph == 1)
1527 # Close the open paragraph
1528 print OUTPUT $fmt[1];
1529 $open_paragraph = 0;
1531 # Start raw output
1532 print OUTPUT $fmt[12];
1533 $open_raw = 1;
1535 if ($opt_output_format eq "")
1537 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1539 print OUTPUT $_,"\n";
1541 else
1543 if ($opt_output_format eq "h")
1545 # Link to the file in WineHQ cvs
1546 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1548 # Highlight strings
1549 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1550 # Highlight literal chars
1551 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1552 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1553 # Highlight numeric constants
1554 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1556 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1557 # FIXME: Using bullet points for leading '-' would look nicer.
1558 if ($open_paragraph == 1)
1560 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1561 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1563 else
1565 s/^(\-)/$fmt[4]$1$fmt[5]/;
1566 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1569 if ($opt_output_format eq "h")
1571 # Html uses links for API calls
1572 s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1573 # Index references
1574 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1575 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1576 # And references to COM objects (hey, they'll get documented one day)
1577 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1578 # Convert any web addresses to real links
1579 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1581 else
1583 if ($opt_output_format eq "")
1585 # Give the man section for API calls
1586 s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1588 else
1590 # Highlight API calls
1591 s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1594 # And references to COM objects
1595 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1598 if ($open_raw == 1)
1600 # Finish the raw output
1601 print OUTPUT $fmt[13];
1602 $open_raw = 0;
1605 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1607 # Start of a new section
1608 if ($open_paragraph == 1)
1610 if ($param_docs == 1)
1612 print OUTPUT $fmt[17],$fmt[15];
1614 else
1616 print OUTPUT $fmt[1];
1618 $open_paragraph = 0;
1620 output_api_section_start($comment,$_);
1621 if ( /^PARAMS$/ )
1623 print OUTPUT $fmt[14];
1624 $param_docs = 1;
1626 else
1628 #print OUTPUT $fmt[15];
1629 $param_docs = 0;
1632 elsif ( /^$/ )
1634 # Empty line, indicating a new paragraph
1635 if ($open_paragraph == 1)
1637 if ($param_docs == 0)
1639 print OUTPUT $fmt[1];
1640 $open_paragraph = 0;
1644 else
1646 if ($param_docs == 1)
1648 if ($open_paragraph == 1)
1650 # For parameter docs, put each parameter into a new paragraph/table row
1651 print OUTPUT $fmt[17];
1652 $open_paragraph = 0;
1654 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1656 else
1658 # Within paragraph lines, prevent lines running together
1659 $_ = $_." ";
1662 # Format parameter names where they appear in the comment
1663 for my $parameter_name (@parameter_names)
1665 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\=\/])/$1$fmt[8]$2$fmt[9]$3/g;
1667 # Structure dereferences include the dereferenced member
1668 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1669 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1671 if ($open_paragraph == 0)
1673 if ($param_docs == 1)
1675 print OUTPUT $fmt[16];
1677 else
1679 print OUTPUT $fmt[0];
1681 $open_paragraph = 1;
1683 # Anything in all uppercase on its own gets emphasised
1684 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1686 print OUTPUT $_;
1690 if ($open_raw == 1)
1692 print OUTPUT $fmt[13];
1694 if ($open_paragraph == 1)
1696 print OUTPUT $fmt[1];
1700 # Create the master index file
1701 sub output_master_index_files()
1703 if ($opt_output_format eq "")
1705 return; # No master index for man pages
1708 if ($opt_output_format eq "h")
1710 # Append the index entries to the output db of index entries
1711 my $output_file = $opt_output_directory."/index.db";
1712 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1713 for (@index_entries_list)
1715 $_ =~ s/A\,/\,/;
1716 print INDEXDB $_."\n";
1718 close(INDEXDB);
1721 # Use the comment output functions for consistency
1722 my $comment =
1724 FILE => "",
1725 COMMENT_NAME => "The Wine Api Guide",
1726 ALT_NAME => "The Wine Api Guide",
1727 DLL_NAME => "",
1728 ORDINAL => "",
1729 RETURNS => "",
1730 PROTOTYPE => [],
1731 TEXT => [],
1734 if ($opt_output_format eq "s")
1736 $comment->{COMMENT_NAME} = "Introduction";
1737 $comment->{ALT_NAME} = "Introduction",
1739 elsif ($opt_output_format eq "h")
1741 @{$comment->{TEXT}} = (
1742 "NAME",
1743 $comment->{COMMENT_NAME},
1744 "INTRODUCTION",
1748 # Create the initial comment text
1749 push (@{$comment->{TEXT}},
1750 "This document describes the Api calls made available",
1751 "by Wine. They are grouped by the dll that exports them.",
1753 "Please do not edit this document, since it is generated automatically",
1754 "from the Wine source code tree. Details on updating this documentation",
1755 "are given in the \"Wine Developers Guide\".",
1756 "CONTRIBUTORS",
1757 "Api documentation is generally written by the person who ",
1758 "implements a given Api call. Authors of each dll are listed in the overview ",
1759 "section for that dll. Additional contributors who have updated source files ",
1760 "but have not entered their names in a copyright statement are noted by an ",
1761 "entry in the file \"Changelog\" from the Wine source code distribution.",
1765 # Read in all dlls from the database of dll names
1766 my $input_file = $opt_output_directory."/dlls.db";
1767 my @dlls = `cat $input_file|sort|uniq`;
1769 if ($opt_output_format eq "h")
1771 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1772 push (@{$comment->{TEXT}},
1773 "INDEX",
1774 "For an alphabetical listing of the functions available, please click the ",
1775 "first letter of the functions name below:","",
1776 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1777 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1778 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1779 "DLLS",
1780 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1783 # Add the dlls to the comment
1784 for (@dlls)
1786 $_ =~ s/(\..*)?\n/\(\)/;
1787 push (@{$comment->{TEXT}}, $_, "");
1789 output_open_api_file("index");
1791 elsif ($opt_output_format eq "s")
1793 # Just write this as the initial blurb, with a chapter heading
1794 output_open_api_file("blurb");
1795 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1798 # Write out the document
1799 output_api_header($comment);
1800 output_api_comment($comment);
1801 output_api_footer($comment);
1802 if ($opt_output_format eq "s")
1804 print OUTPUT "</chapter>\n" # finish the chapter
1806 output_close_api_file();
1808 if ($opt_output_format eq "s")
1810 output_sgml_master_file(\@dlls);
1811 return;
1813 if ($opt_output_format eq "h")
1815 output_html_index_files();
1816 output_html_stylesheet();
1817 return;
1821 # Write the master wine-api.sgml, linking it to each dll.
1822 sub output_sgml_master_file($)
1824 my $dlls = shift;
1826 output_open_api_file("wine-api");
1827 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1828 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1829 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1831 # List the entities
1832 for (@$dlls)
1834 $_ =~ s/(\..*)?\n//;
1835 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1838 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1839 print OUTPUT " &blurb;\n";
1841 for (@$dlls)
1843 print OUTPUT " &",$_,";\n"
1845 print OUTPUT "\n\n</book>\n";
1847 output_close_api_file();
1850 # Produce the sgml for the dll chapter from the generated files
1851 sub output_sgml_dll_file($)
1853 my $spec_details = shift;
1855 # Make a list of all the documentation files to include
1856 my $exports = $spec_details->{EXPORTS};
1857 my @source_files = ();
1858 for (@$exports)
1860 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1861 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1862 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1864 # A documented function
1865 push (@source_files,@$_[3]);
1869 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1871 @source_files = sort @source_files;
1873 # create a new chapter for this dll
1874 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1875 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1876 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1877 output_close_api_file();
1879 # Add the sorted documentation, cleaning up as we go
1880 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1881 for (@source_files)
1883 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1884 `rm -f $opt_output_directory/$_.sgml`;
1887 # close the chapter, and overwite the dll source
1888 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1889 print OUTPUT "</chapter>\n";
1890 close OUTPUT;
1891 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1894 # Write the html index files containing the function names
1895 sub output_html_index_files()
1897 if ($opt_output_format ne "h")
1899 return;
1902 my @letters = ('_', 'A' .. 'Z');
1904 # Read in all functions
1905 my $input_file = $opt_output_directory."/index.db";
1906 my @funcs = `cat $input_file|sort|uniq`;
1908 for (@letters)
1910 my $letter = $_;
1911 my $comment =
1913 FILE => "",
1914 COMMENT_NAME => "",
1915 ALT_NAME => "",
1916 DLL_NAME => "",
1917 ORDINAL => "",
1918 RETURNS => "",
1919 PROTOTYPE => [],
1920 TEXT => [],
1923 $comment->{COMMENT_NAME} = $letter." Functions";
1924 $comment->{ALT_NAME} = $letter." Functions";
1926 push (@{$comment->{TEXT}},
1927 "NAME",
1928 $comment->{COMMENT_NAME},
1929 "FUNCTIONS"
1932 # Add the functions to the comment
1933 for (@funcs)
1935 my $first_char = substr ($_, 0, 1);
1936 $first_char = uc $first_char;
1938 if ($first_char eq $letter)
1940 my $name = $_;
1941 my $file;
1942 $name =~ s/(^.*?)\,(.*?)\n/$1/;
1943 $file = $2;
1944 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
1948 # Write out the document
1949 output_open_api_file($letter);
1950 output_api_header($comment);
1951 output_api_comment($comment);
1952 output_api_footer($comment);
1953 output_close_api_file();
1957 # Output the stylesheet for HTML output
1958 sub output_html_stylesheet()
1960 if ($opt_output_format ne "h")
1962 return;
1965 my $css;
1966 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
1968 * Default styles for Wine HTML Documentation.
1970 * This style sheet should be altered to suit your needs/taste.
1972 BODY { /* Page body */
1973 background-color: white;
1974 color: black;
1975 font-family: Tahoma,sans-serif;
1976 font-style: normal;
1977 font-size: 10pt;
1979 a:link { color: #4444ff; } /* Links */
1980 a:visited { color: #333377 }
1981 a:active { color: #0000dd }
1982 H2.section { /* Section Headers */
1983 font-family: sans-serif;
1984 color: #777777;
1985 background-color: #F0F0FE;
1986 margin-left: 0.2in;
1987 margin-right: 1.0in;
1989 b.func_name { /* Function Name */
1990 font-size: 10pt;
1991 font-style: bold;
1993 i.dll_ord { /* Italicised DLL+ordinal */
1994 color: #888888;
1995 font-family: sans-serif;
1996 font-size: 8pt;
1998 p { /* Paragraphs */
1999 margin-left: 0.5in;
2000 margin-right: 0.5in;
2002 table { /* tables */
2003 margin-left: 0.5in;
2004 margin-right: 0.5in;
2006 pre.proto /* API Function prototype */
2008 border-style: solid;
2009 border-width: 1px;
2010 border-color: #777777;
2011 background-color: #F0F0BB;
2012 color: black;
2013 font-size: 10pt;
2014 vertical-align: top;
2015 margin-left: 0.5in;
2016 margin-right: 1.0in;
2018 pre.raw { /* Raw text output */
2019 margin-left: 0.6in;
2020 margin-right: 1.1in;
2021 background-color: #8080DC;
2023 tt.param { /* Parameter name */
2024 font-style: italic;
2025 color: blue;
2027 tt.const { /* Constant */
2028 color: red;
2030 i.in_out { /* In/Out */
2031 font-size: 8pt;
2032 color: grey;
2034 tt.coderef { /* Code in description text */
2035 color: darkgreen;
2037 b.emp /* Emphasis */ {
2038 font-style: bold;
2039 color: darkblue;
2041 i.footer { /* Footer */
2042 font-family: sans-serif;
2043 font-size: 6pt;
2044 color: darkgrey;
2046 HERE_TARGET
2048 my $output_file = "$opt_output_directory/apidoc.css";
2049 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2050 print CSS $css;
2051 close(CSS);
2055 sub usage()
2057 print "\nCreate API Documentation from Wine source code.\n\n",
2058 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
2059 "Where: <spec> is a .spec file giving a DLL's exports.\n",
2060 " <include> is an include directory used by the DLL.\n",
2061 " <source> is a source file of the DLL.\n",
2062 " The above can be given multiple times on the command line, as appropriate.\n",
2063 "Options:\n",
2064 " -Th : Output HTML instead of a man page\n",
2065 " -Ts : Output SGML (Docbook source) instead of a man page\n",
2066 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
2067 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
2068 " -e : Output \"FIXME\" documentation from empty comments.\n",
2069 " -v : Verbosity. Can be given more than once for more detail.\n";
2074 # Main
2077 # Print usage if we're called with no args
2078 if( @ARGV == 0)
2080 usage();
2083 # Process command line options
2084 while(defined($_ = shift @ARGV))
2086 if( s/^-// )
2088 # An option.
2089 for ($_)
2091 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
2092 s/^S// && do { $opt_manual_section = $_; last; };
2093 /^Th$/ && do { $opt_output_format = "h"; last; };
2094 /^Ts$/ && do { $opt_output_format = "s"; last; };
2095 /^v$/ && do { $opt_verbose++; last; };
2096 /^e$/ && do { $opt_output_empty = 1; last; };
2097 /^L$/ && do { last; };
2098 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
2099 s/^I// && do { if ($_ ne ".") {
2100 my $include = $_."/*.h";
2101 $include =~ s/\/\//\//g;
2102 my $have_headers = `ls $include >/dev/null 2>&1`;
2103 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
2105 last;
2107 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
2108 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
2109 $opt_wine_root_dir =~ s/\n//;
2110 $opt_wine_root_dir =~ s/\/\//\//g;
2111 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
2112 last;
2114 die "Unrecognised option $_\n";
2117 else
2119 # A source file.
2120 push (@opt_source_file_list, $_);
2124 # Remove duplicate include directories
2125 my %htmp;
2126 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
2128 if ($opt_verbose > 3)
2130 print "Output dir:'".$opt_output_directory."'\n";
2131 print "Section :'".$opt_manual_section."'\n";
2132 print "Format :'".$opt_output_format."'\n";
2133 print "Root :'".$opt_wine_root_dir."'\n";
2134 print "Spec files:'@opt_spec_file_list'\n";
2135 print "Includes :'@opt_header_file_list'\n";
2136 print "Sources :'@opt_source_file_list'\n";
2139 if (@opt_spec_file_list == 0)
2141 exit 0; # Don't bother processing non-dll files
2144 # Read in each .spec files exports and other details
2145 while(my $spec_file = shift @opt_spec_file_list)
2147 process_spec_file($spec_file);
2150 if ($opt_verbose > 3)
2152 foreach my $spec_file ( keys %spec_files )
2154 print "in '$spec_file':\n";
2155 my $spec_details = $spec_files{$spec_file}[0];
2156 my $exports = $spec_details->{EXPORTS};
2157 for (@$exports)
2159 print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n";
2164 # Extract and output the comments from each source file
2165 while(defined($_ = shift @opt_source_file_list))
2167 process_source_file($_);
2170 # Write the index files for each spec
2171 process_index_files();
2173 # Write the master index file
2174 output_master_index_files();
2176 exit 0;