Completely disable unused function show_image().
[wine/wine-gecko.git] / tools / c2man.pl
blob67c9e1b33d28415a21b076cbb315c775f1ff3f84
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 # Generate the list of exported entries for the dll
70 sub process_spec_file($)
72 my $spec_name = shift;
73 my $dll_name = $spec_name;
74 $dll_name =~ s/\..*//; # Strip the file extension
75 my $uc_dll_name = uc $dll_name;
77 my $spec_details =
79 NAME => $spec_name,
80 DLL_NAME => $dll_name,
81 NUM_EXPORTS => 0,
82 NUM_STUBS => 0,
83 NUM_FUNCS => 0,
84 NUM_FORWARDS => 0,
85 NUM_VARS => 0,
86 NUM_DOCS => 0,
87 CONTRIBUTORS => [ ],
88 SOURCES => [ ],
89 DESCRIPTION => [ ],
90 EXPORTS => [ ],
91 EXPORTED_NAMES => { },
92 IMPLEMENTATION_NAMES => { },
93 EXTRA_COMMENTS => [ ],
94 CURRENT_EXTRA => [ ] ,
97 if ($opt_verbose > 0)
99 print "Processing ".$spec_name."\n";
102 # We allow opening to fail just to cater for the peculiarities of
103 # the Wine build system. This doesn't hurt, in any case
104 open(SPEC_FILE, "<$spec_name") || return;
106 while(<SPEC_FILE>)
108 s/^\s+//; # Strip leading space
109 s/\s+\n$/\n/; # Strip trailing space
110 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
111 s/\s*#.*//; # Strip comments
112 s/\(.*\)/ /; # Strip arguments
113 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
114 s/\n$//; # Strip newline
116 my $flags = 0;
117 if( /\-noname/ )
119 $flags |= $FLAG_NONAME;
121 if( /\-i386/ )
123 $flags |= $FLAG_I386;
125 if( /\-register/ )
127 $flags |= $FLAG_REGISTER;
129 s/ \-[a-z0-9]+//g; # Strip flags
131 if( /^(([0-9]+)|@) / )
133 # This line contains an exported symbol
134 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
136 for ($call_convention)
138 /^(cdecl|stdcall|varargs|pascal)$/
139 && do { $spec_details->{NUM_FUNCS}++; last; };
140 /^(variable|equate)$/
141 && do { $spec_details->{NUM_VARS}++; last; };
142 /^(extern)$/
143 && do { $spec_details->{NUM_FORWARDS}++; last; };
144 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
145 if ($opt_verbose > 0)
147 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
149 last;
152 # Convert ordinal only names so we can find them later
153 if ($exported_name eq "@")
155 $exported_name = $uc_dll_name.'_'.$ordinal;
157 if (!defined($implementation_name))
159 $implementation_name = $exported_name;
161 if ($implementation_name eq "")
163 $implementation_name = $exported_name;
166 if ($implementation_name =~ /(.*?)\./)
168 $call_convention = "forward"; # Referencing a function from another dll
169 $spec_details->{NUM_FUNCS}--;
170 $spec_details->{NUM_FORWARDS}++;
173 # Add indices for the exported and implementation names
174 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
175 if ($implementation_name ne $exported_name)
177 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
180 # Add the exported entry
181 $spec_details->{NUM_EXPORTS}++;
182 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
183 push (@{$spec_details->{EXPORTS}},[@export]);
186 close(SPEC_FILE);
188 # Add this .spec files details to the list of .spec files
189 $spec_files{$uc_dll_name} = [$spec_details];
192 # Read each source file, extract comments, and generate API documentation if appropriate
193 sub process_source_file($)
195 my $source_file = shift;
196 my $source_details =
198 CONTRIBUTORS => [ ],
199 DEBUG_CHANNEL => "",
201 my $comment =
203 FILE => $source_file,
204 COMMENT_NAME => "",
205 ALT_NAME => "",
206 DLL_NAME => "",
207 ORDINAL => "",
208 RETURNS => "",
209 PROTOTYPE => [],
210 TEXT => [],
212 my $parse_state = 0;
213 my $ignore_blank_lines = 1;
214 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
216 if ($opt_verbose > 0)
218 print "Processing ".$source_file."\n";
220 open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n";
222 # Add this source file to the list of source files
223 $source_files{$source_file} = [$source_details];
225 while(<SOURCE_FILE>)
227 s/\n$//; # Strip newline
228 s/^\s+//; # Strip leading space
229 s/\s+$//; # Strip trailing space
230 if (! /^\*\|/ )
232 # Strip multiple tabs & spaces to a single space
233 s/\s+/ /g;
236 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
238 # Extract a contributor to this file
239 my $contributor = $2;
240 $contributor =~ s/ *$//;
241 $contributor =~ s/^by //;
242 $contributor =~ s/\.$//;
243 $contributor =~ s/ (for .*)/ \($1\)/;
244 if ($contributor ne "")
246 if ($opt_verbose > 3)
248 print "Info: Found contributor:'".$contributor."'\n";
250 push (@{$source_details->{CONTRIBUTORS}},$contributor);
253 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
255 # Extract the debug channel to use
256 if ($opt_verbose > 3)
258 print "Info: Found debug channel:'".$1."'\n";
260 $source_details->{DEBUG_CHANNEL} = $1;
263 if ($parse_state == 0) # Searching for a comment
265 if ( /^\/\**$/ )
267 # Found a comment start
268 $comment->{COMMENT_NAME} = "";
269 $comment->{ALT_NAME} = "";
270 $comment->{DLL_NAME} = "";
271 $comment->{ORDINAL} = "";
272 $comment->{RETURNS} = "";
273 $comment->{PROTOTYPE} = [];
274 $comment->{TEXT} = [];
275 $ignore_blank_lines = 1;
276 $extra_comment = 0;
277 $parse_state = 3;
280 elsif ($parse_state == 1) # Reading in a comment
282 if ( /^\**\// )
284 # Found the end of the comment
285 $parse_state = 2;
287 elsif ( s/^\*\|/\|/ )
289 # A line of comment not meant to be pre-processed
290 push (@{$comment->{TEXT}},$_); # Add the comment text
292 elsif ( s/^ *\** *// )
294 # A line of comment, starting with an asterisk
295 if ( /^[A-Z]+$/ || $_ eq "")
297 # This is a section start, so skip blank lines before and after it.
298 my $last_line = pop(@{$comment->{TEXT}});
299 if (defined($last_line) && $last_line ne "")
301 # Put it back
302 push (@{$comment->{TEXT}},$last_line);
304 if ( /^[A-Z]+$/ )
306 $ignore_blank_lines = 1;
308 else
310 $ignore_blank_lines = 0;
314 if ($ignore_blank_lines == 0 || $_ ne "")
316 push (@{$comment->{TEXT}},$_); # Add the comment text
319 else
321 # This isn't a well formatted comment: look for the next one
322 $parse_state = 0;
325 elsif ($parse_state == 2) # Finished reading in a comment
327 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
328 /.*?\(/ )
330 # Comment is followed by a function definition
331 $parse_state = 4; # Fall through to read prototype
333 else
335 # Allow cpp directives and blank lines between the comment and prototype
336 if ($extra_comment == 1)
338 # An extra comment not followed by a function definition
339 $parse_state = 5; # Fall through to process comment
341 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
343 # This isn't a well formatted comment: look for the next one
344 if ($opt_verbose > 1)
346 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
348 $parse_state = 0;
352 elsif ($parse_state == 3) # Reading in the first line of a comment
354 s/^ *\** *//;
355 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
357 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
358 $comment->{COMMENT_NAME} = $1;
359 $comment->{DLL_NAME} = uc $3;
360 $comment->{ORDINAL} = $4;
361 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
362 $parse_state = 1;
364 elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ )
366 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
367 $comment->{COMMENT_NAME} = $1;
368 $comment->{DLL_NAME} = uc $2;
369 $comment->{ORDINAL} = "";
370 $extra_comment = 1;
371 $parse_state = 1;
373 else
375 # This isn't a well formatted comment: look for the next one
376 $parse_state = 0;
380 if ($parse_state == 4) # Reading in the function definition
382 push (@{$comment->{PROTOTYPE}},$_);
383 # Strip comments from the line before checking for ')'
384 my $stripped_line = $_;
385 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
386 if ( $stripped_line =~ /\)/ )
388 # Strip a blank last line
389 my $last_line = pop(@{$comment->{TEXT}});
390 if (defined($last_line) && $last_line ne "")
392 # Put it back
393 push (@{$comment->{TEXT}},$last_line);
396 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
398 # Create a 'not implemented' comment
399 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
401 $parse_state = 5;
405 if ($parse_state == 5) # Processing the comment
407 # Process it, if it has any text
408 if (@{$comment->{TEXT}} > 0)
410 if ($extra_comment == 1)
412 process_extra_comment($comment);
414 else
416 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
417 process_comment($comment);
420 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
422 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
424 $parse_state = 0;
427 close(SOURCE_FILE);
430 # Standardise a comments text for consistency
431 sub process_comment_text($)
433 my $comment = shift;
434 my $i = 0;
436 for (@{$comment->{TEXT}})
438 if (! /^\|/ )
440 # Map I/O values. These come in too many formats to standardise now....
441 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
442 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
443 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
444 # TRUE/FALSE/NULL are defines, capitilise them
445 s/True|true/TRUE/g;
446 s/False|false/FALSE/g;
447 s/Null|null/NULL/g;
448 # Preferred capitalisations
449 s/ wine| WINE/ Wine/g;
450 s/ API | api / Api /g;
451 s/DLL|Dll/dll /g;
452 s/ URL | url / Url /g;
453 s/WIN16|win16/Win16/g;
454 s/WIN32|win32/Win32/g;
455 s/WIN64|win64/Win64/g;
456 s/ ID | id / Id /g;
457 # Grammar
458 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
459 s/ \:/\:/g; # Colons to the left
460 s/ \;/\;/g; # Semi-colons too
461 # Common idioms
462 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
463 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
464 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
465 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
466 # Trademarks
467 s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g;
468 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
469 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
470 s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
471 # Abbreviations
472 s/( char )/ character /g;
473 s/( chars )/ characters /g;
474 s/( info )/ information /g;
475 s/( app )/ application /g;
476 s/( apps )/ applications /g;
477 s/( exe )/ executable /g;
478 s/( ptr )/ pointer /g;
479 s/( obj )/ object /g;
480 s/( err )/ error /g;
481 s/( bool )/ boolean /g;
482 # Punctuation
483 if ( /\[I|\[O/ && ! /\.$/ )
485 $_ = $_."."; # Always have a full stop at the end of parameter desc.
487 elsif ($i > 0 && /^[A-Z]*$/ &&
488 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
489 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
492 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
494 # Paragraphs always end with a full stop
495 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
499 $i++;
503 # Standardise our comment and output it if it is suitable.
504 sub process_comment($)
506 my $comment = shift;
508 # Don't process this comment if the function isn't exported
509 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
511 if (!defined($spec_details))
513 if ($opt_verbose > 2)
515 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
516 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
518 return;
521 if ($comment->{COMMENT_NAME} eq "@")
523 my $found = 0;
525 # Find the name from the .spec file
526 for (@{$spec_details->{EXPORTS}})
528 if (@$_[0] eq $comment->{ORDINAL})
530 $comment->{COMMENT_NAME} = @$_[2];
531 $found = 1;
535 if ($found == 0)
537 # Create an implementation name
538 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
542 my $exported_names = $spec_details->{EXPORTED_NAMES};
543 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
544 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
546 if (!defined($export_index))
548 # Perhaps the comment uses the implementation name?
549 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
551 if (!defined($export_index))
553 # This function doesn't appear to be exported. hmm.
554 if ($opt_verbose > 2)
556 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
557 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
559 return;
562 # When the function is exported twice we have the second name below the first
563 # (you see this a lot in ntdll, but also in some other places).
564 my $first_line = ${@{$comment->{TEXT}}}[1];
566 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
568 # Found a second name - mark it as documented
569 my $alt_index = $exported_names->{$1};
570 if (defined($alt_index))
572 if ($opt_verbose > 2)
574 print "Info: Found alternate name '",$1,"\n";
576 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
577 @$alt_export[4] |= $FLAG_DOCUMENTED;
578 $spec_details->{NUM_DOCS}++;
579 ${@{$comment->{TEXT}}}[1] = "";
583 if (@{$spec_details->{CURRENT_EXTRA}})
585 # We have an extra comment that might be related to this one
586 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
587 my $current_name = $current_comment->{COMMENT_NAME};
588 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
590 if ($opt_verbose > 2)
592 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
594 # Add a reference to this comment to our extra comment
595 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
599 # We want our docs generated using the implementation name, so they are unique
600 my $export = @{$spec_details->{EXPORTS}}[$export_index];
601 $comment->{COMMENT_NAME} = @$export[3];
602 $comment->{ALT_NAME} = @$export[2];
604 # Mark the function as documented
605 $spec_details->{NUM_DOCS}++;
606 @$export[4] |= $FLAG_DOCUMENTED;
608 # This file is used by the DLL - Make sure we get our contributors right
609 push (@{$spec_details->{SOURCES}},$comment->{FILE});
611 # If we have parameter comments in the prototype, extract them
612 my @parameter_comments;
613 for (@{$comment->{PROTOTYPE}})
615 s/ *\, */\,/g; # Strip spaces from around commas
617 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
619 my $parameter_comment = $2;
620 if (!$parameter_comment =~ /^\[/ )
622 # Add [IO] markers so we format the comment correctly
623 $parameter_comment = "[fixme] ".$parameter_comment;
625 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
627 # Add the parameter name
628 $parameter_comment = $2." ".$parameter_comment;
630 push (@parameter_comments, $parameter_comment);
634 # If we extracted any prototype comments, add them to the comment text.
635 if (@parameter_comments)
637 @parameter_comments = ("PARAMS", @parameter_comments);
638 my @new_comment = ();
639 my $inserted_params = 0;
641 for (@{$comment->{TEXT}})
643 if ( $inserted_params == 0 && /^[A-Z]+$/ )
645 # Found a section header, so this is where we insert
646 push (@new_comment, @parameter_comments);
647 $inserted_params = 1;
649 push (@new_comment, $_);
651 if ($inserted_params == 0)
653 # Add them to the end
654 push (@new_comment, @parameter_comments);
656 $comment->{TEXT} = [@new_comment];
659 if ($opt_fussy == 1 && $opt_output_empty == 0)
661 # Reject any comment that doesn't have a description or a RETURNS section.
662 # This is the default for now, 'coz many comments aren't suitable.
663 my $found_returns = 0;
664 my $found_description_text = 0;
665 my $in_description = 0;
666 for (@{$comment->{TEXT}})
668 if ( /^RETURNS$/ )
670 $found_returns = 1;
671 $in_description = 0;
673 elsif ( /^DESCRIPTION$/ )
675 $in_description = 1;
677 elsif ($in_description == 1)
679 if ( !/^[A-Z]+$/ )
681 # Don't reject comments that refer to another doc (e.g. A/W)
682 if ( /^See ([A-Za-z0-9_]+)\.$/ )
684 if ($comment->{COMMENT_NAME} =~ /W$/ )
686 # This is probably a Unicode version of an Ascii function.
687 # Create the Ascii name and see if its been documented
688 my $ascii_name = $comment->{COMMENT_NAME};
689 $ascii_name =~ s/W$/A/;
691 my $ascii_export_index = $exported_names->{$ascii_name};
693 if (!defined($ascii_export_index))
695 $ascii_export_index = $implementation_names->{$ascii_name};
697 if (!defined($ascii_export_index))
699 if ($opt_verbose > 2)
701 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
704 else
706 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
707 if (@$ascii_export[4] & $FLAG_DOCUMENTED)
709 # Flag these functions as an A/W pair
710 @$ascii_export[4] |= $FLAG_APAIR;
711 @$export[4] |= $FLAG_WPAIR;
715 $found_returns = 1;
717 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
719 @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
720 $found_returns = 1;
722 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
724 @$export[4] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
725 $found_returns = 1;
727 $found_description_text = 1;
729 else
731 $in_description = 0;
735 if ($found_returns == 0 || $found_description_text == 0)
737 if ($opt_verbose > 2)
739 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
740 "description and/or RETURNS section, skipping\n";
742 $spec_details->{NUM_DOCS}--;
743 @$export[4] &= ~$FLAG_DOCUMENTED;
744 return;
748 process_comment_text($comment);
750 # Strip the prototypes return value, call convention, name and brackets
751 # (This leaves it as a list of types and names, or empty for void functions)
752 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
753 $prototype =~ s/ / /g;
755 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
757 $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
758 $comment->{RETURNS} = $1;
760 else
762 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\( *(.*)/$3/;
763 $comment->{RETURNS} = $1;
766 $prototype =~ s/ *\).*//; # Strip end bracket
767 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
768 $prototype =~ s/ *\, */\,/g; # Strip space around commas
769 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
770 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
771 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
773 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
775 # Find header file
776 my $h_file = "";
777 if (@$export[4] & $FLAG_NONAME)
779 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
781 else
783 if ($comment->{COMMENT_NAME} ne "")
785 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
786 $tmp = `$tmp`;
787 my $exit_value = $? >> 8;
788 if ($exit_value == 0)
790 $tmp =~ s/\n.*//g;
791 if ($tmp ne "")
793 $h_file = `basename $tmp`;
797 elsif ($comment->{ALT_NAME} ne "")
799 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
800 $tmp = `$tmp`;
801 my $exit_value = $? >> 8;
802 if ($exit_value == 0)
804 $tmp =~ s/\n.*//g;
805 if ($tmp ne "")
807 $h_file = `basename $tmp`;
811 $h_file =~ s/^ *//;
812 $h_file =~ s/\n//;
813 if ($h_file eq "")
815 $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine."
817 else
819 $h_file = "Defined in \"".$h_file."\".";
823 # Find source file
824 my $c_file = $comment->{FILE};
825 if ($opt_wine_root_dir ne "")
827 my $cfile = $pwd."/".$c_file; # Current dir + file
828 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
829 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
830 $cfile =~ s/\n//; # Strip newline
831 my $newfile = $c_file;
832 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
833 $cfile = $cfile."/".$newfile; # Append filename to base path
834 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
835 $cfile =~ s/\/\//\//g; # Remove any double slashes
836 $cfile =~ s/^\/+//; # Strip initial directory slash
837 $c_file = $cfile;
839 $c_file = "Implemented in \"".$c_file."\".";
841 # Add the implementation details
842 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
844 if (@$export[4] & $FLAG_I386)
846 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
848 if (@$export[4] & $FLAG_REGISTER)
850 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
851 "For more details, please read the source code.");
853 my $source_details = $source_files{$comment->{FILE}}[0];
854 if ($source_details->{DEBUG_CHANNEL} ne "")
856 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
859 # Write out the documentation for the API
860 output_comment($comment)
863 # process our extra comment and output it if it is suitable.
864 sub process_extra_comment($)
866 my $comment = shift;
868 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
870 if (!defined($spec_details))
872 if ($opt_verbose > 2)
874 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
875 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
877 return;
880 # Check first to see if this is documentation for the DLL.
881 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
883 if ($opt_verbose > 2)
885 print "Info: Found DLL documentation\n";
887 for (@{$comment->{TEXT}})
889 push (@{$spec_details->{DESCRIPTION}}, $_);
891 return;
894 # Add the comment to the DLL page as a link
895 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
897 # If we have a prototype, process as a regular comment
898 if (@{$comment->{PROTOTYPE}})
900 $comment->{ORDINAL} = "@";
902 # Add an index for the comment name
903 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
905 # Add a fake exported entry
906 $spec_details->{NUM_EXPORTS}++;
907 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
908 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
909 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
910 push (@{$spec_details->{EXPORTS}},[@export]);
911 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
912 process_comment($comment);
913 return;
916 if ($opt_verbose > 0)
918 print "Processing ",$comment->{COMMENT_NAME},"\n";
921 if (@{$spec_details->{CURRENT_EXTRA}})
923 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
925 if ($opt_verbose > 0)
927 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
929 # Output the current comment
930 process_comment_text($current_comment);
931 output_open_api_file($current_comment->{COMMENT_NAME});
932 output_api_header($current_comment);
933 output_api_name($current_comment);
934 output_api_comment($current_comment);
935 output_api_footer($current_comment);
936 output_close_api_file();
939 if ($opt_verbose > 2)
941 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
944 my $comment_copy =
946 FILE => $comment->{FILE},
947 COMMENT_NAME => $comment->{COMMENT_NAME},
948 ALT_NAME => $comment->{ALT_NAME},
949 DLL_NAME => $comment->{DLL_NAME},
950 ORDINAL => $comment->{ORDINAL},
951 RETURNS => $comment->{RETURNS},
952 PROTOTYPE => [],
953 TEXT => [],
956 for (@{$comment->{TEXT}})
958 push (@{$comment_copy->{TEXT}}, $_);
960 # Set this comment to be the current extra comment
961 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
964 # Write a standardised comment out in the appropriate format
965 sub output_comment($)
967 my $comment = shift;
969 if ($opt_verbose > 0)
971 print "Processing ",$comment->{COMMENT_NAME},"\n";
974 if ($opt_verbose > 4)
976 print "--PROTO--\n";
977 for (@{$comment->{PROTOTYPE}})
979 print "'".$_."'\n";
982 print "--COMMENT--\n";
983 for (@{$comment->{TEXT} })
985 print $_."\n";
989 output_open_api_file($comment->{COMMENT_NAME});
990 output_api_header($comment);
991 output_api_name($comment);
992 output_api_synopsis($comment);
993 output_api_comment($comment);
994 output_api_footer($comment);
995 output_close_api_file();
998 # Write out an index file for each .spec processed
999 sub process_index_files()
1001 foreach my $spec_file (keys %spec_files)
1003 my $spec_details = $spec_files{$spec_file}[0];
1004 if (defined ($spec_details->{DLL_NAME}))
1006 if (@{$spec_details->{CURRENT_EXTRA}})
1008 # We have an unwritten extra comment, write it
1009 my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1010 process_extra_comment($current_comment);
1011 @{$spec_details->{CURRENT_EXTRA}} = ();
1013 output_spec($spec_details);
1018 # Write a spec files documentation out in the appropriate format
1019 sub output_spec($)
1021 my $spec_details = shift;
1023 if ($opt_verbose > 2)
1025 print "Writing:",$spec_details->{DLL_NAME},"\n";
1028 # Use the comment output functions for consistency
1029 my $comment =
1031 FILE => $spec_details->{DLL_NAME},
1032 COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1033 ALT_NAME => $spec_details->{DLL_NAME},
1034 DLL_NAME => "",
1035 ORDINAL => "",
1036 RETURNS => "",
1037 PROTOTYPE => [],
1038 TEXT => [],
1040 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1041 $spec_details->{NUM_FUNCS};
1042 my $percent_implemented = 0;
1043 if ($total_implemented)
1045 $percent_implemented = $total_implemented /
1046 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1048 $percent_implemented = int($percent_implemented);
1049 my $percent_documented = 0;
1050 if ($spec_details->{NUM_DOCS})
1052 # Treat forwards and data as documented funcs for statistics
1053 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1054 $percent_documented = int($percent_documented);
1057 # Make a list of the contributors to this DLL. Do this only for the source
1058 # files that make up the DLL, because some directories specify multiple dlls.
1059 my @contributors;
1061 for (@{$spec_details->{SOURCES}})
1063 my $source_details = $source_files{$_}[0];
1064 for (@{$source_details->{CONTRIBUTORS}})
1066 push (@contributors, $_);
1070 my %saw;
1071 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1072 @contributors = sort @contributors;
1074 # Remove duplicates and blanks
1075 for(my $i=0; $i<@contributors; $i++)
1077 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1079 $contributors[$i-1] = $contributors[$i];
1082 undef %saw;
1083 @contributors = grep(!$saw{$_}++, @contributors);
1085 if ($opt_verbose > 3)
1087 print "Contributors:\n";
1088 for (@contributors)
1090 print "'".$_."'\n";
1093 my $contribstring = join (", ", @contributors);
1095 # Create the initial comment text
1096 @{$comment->{TEXT}} = (
1097 "NAME",
1098 $comment->{COMMENT_NAME}
1101 # Add the description, if we have one
1102 if (@{$spec_details->{DESCRIPTION}})
1104 push (@{$comment->{TEXT}}, "DESCRIPTION");
1105 for (@{$spec_details->{DESCRIPTION}})
1107 push (@{$comment->{TEXT}}, $_);
1111 # Add the statistics and contributors
1112 push (@{$comment->{TEXT}},
1113 "STATISTICS",
1114 "Forwards: ".$spec_details->{NUM_FORWARDS},
1115 "Variables: ".$spec_details->{NUM_VARS},
1116 "Stubs: ".$spec_details->{NUM_STUBS},
1117 "Functions: ".$spec_details->{NUM_FUNCS},
1118 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1119 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1120 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1121 "CONTRIBUTORS",
1122 "The following people hold copyrights on the source files comprising this dll:",
1124 $contribstring,
1125 "Note: This list may not be complete.",
1126 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1130 if ($opt_output_format eq "h")
1132 # Add the exports to the comment text
1133 push (@{$comment->{TEXT}},"EXPORTS");
1134 my $exports = $spec_details->{EXPORTS};
1135 for (@$exports)
1137 my $line = "";
1139 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1140 if (@$_[1] eq "forward")
1142 my $forward_dll = @$_[3];
1143 $forward_dll =~ s/\.(.*)//;
1144 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1146 elsif (@$_[1] eq "extern")
1148 $line = @$_[2]." (extern)";
1150 elsif (@$_[1] eq "stub")
1152 $line = @$_[2]." (stub)";
1154 elsif (@$_[1] eq "fake")
1156 # Don't add this function here, it gets listed with the extra documentation
1157 if (!(@$_[4] & $FLAG_WPAIR))
1159 # This function should be indexed
1160 push (@index_entries_list, @$_[3].",".@$_[3]);
1163 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1165 $line = @$_[2]." (data)";
1167 else
1169 # A function
1170 if (@$_[4] & $FLAG_DOCUMENTED)
1172 # Documented
1173 $line = @$_[2]." (implemented as ".@$_[3]."())";
1174 if (@$_[2] ne @$_[3])
1176 $line = @$_[2]." (implemented as ".@$_[3]."())";
1178 else
1180 $line = @$_[2]."()";
1182 if (!(@$_[4] & $FLAG_WPAIR))
1184 # This function should be indexed
1185 push (@index_entries_list, @$_[2].",".@$_[3]);
1188 else
1190 $line = @$_[2]." (not documented)";
1193 if ($line ne "")
1195 push (@{$comment->{TEXT}}, $line, "");
1199 # Add links to the extra documentation
1200 if (@{$spec_details->{EXTRA_COMMENTS}})
1202 push (@{$comment->{TEXT}}, "SEE ALSO");
1203 my %htmp;
1204 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1205 for (@{$spec_details->{EXTRA_COMMENTS}})
1207 push (@{$comment->{TEXT}}, $_."()", "");
1211 # The dll entry should also be indexed
1212 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1214 # Write out the document
1215 output_open_api_file($spec_details->{DLL_NAME});
1216 output_api_header($comment);
1217 output_api_comment($comment);
1218 output_api_footer($comment);
1219 output_close_api_file();
1221 # Add this dll to the database of dll names
1222 my $output_file = $opt_output_directory."/dlls.db";
1224 # Append the dllname to the output db of names
1225 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1226 print DLLDB $spec_details->{DLL_NAME},"\n";
1227 close(DLLDB);
1229 if ($opt_output_format eq "s")
1231 output_sgml_dll_file($spec_details);
1232 return;
1237 # OUTPUT FUNCTIONS
1238 # ----------------
1239 # Only these functions know anything about formatting for a specific
1240 # output type. The functions above work only with plain text.
1241 # This is to allow new types of output to be added easily.
1243 # Open the api file
1244 sub output_open_api_file($)
1246 my $output_name = shift;
1247 $output_name = $opt_output_directory."/".$output_name;
1249 if ($opt_output_format eq "h")
1251 $output_name = $output_name.".html";
1253 elsif ($opt_output_format eq "s")
1255 $output_name = $output_name.".sgml";
1257 else
1259 $output_name = $output_name.".".$opt_manual_section;
1261 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1264 # Close the api file
1265 sub output_close_api_file()
1267 close (OUTPUT);
1270 # Output the api file header
1271 sub output_api_header($)
1273 my $comment = shift;
1275 if ($opt_output_format eq "h")
1277 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1278 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1279 print OUTPUT "<HTML>\n<HEAD>\n";
1280 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1281 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1282 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1283 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1285 elsif ($opt_output_format eq "s")
1287 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1288 "<sect1>\n",
1289 "<title>$comment->{COMMENT_NAME}</title>\n";
1291 else
1293 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1294 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1295 "Wine API\" \"Wine API\"\n";
1299 sub output_api_footer($)
1301 if ($opt_output_format eq "h")
1303 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1304 " All trademarks are the property of their respective owners.".
1305 " Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1306 " Generated $date.</i></p>\n</body>\n</html>\n";
1308 elsif ($opt_output_format eq "s")
1310 print OUTPUT "</sect1>\n";
1311 return;
1313 else
1318 sub output_api_section_start($$)
1320 my $comment = shift;
1321 my $section_name = shift;
1323 if ($opt_output_format eq "h")
1325 print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1327 elsif ($opt_output_format eq "s")
1329 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1331 else
1333 print OUTPUT "\n\.SH ",$section_name,"\n";
1337 sub output_api_section_end()
1339 # Not currently required by any output formats
1342 sub output_api_name($)
1344 my $comment = shift;
1346 output_api_section_start($comment,"NAME");
1348 my $dll_ordinal = "";
1349 if ($comment->{ORDINAL} ne "")
1351 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1353 if ($opt_output_format eq "h")
1355 print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1356 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1357 ,$dll_ordinal,"</i></p>\n";
1359 elsif ($opt_output_format eq "s")
1361 print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>",
1362 $dll_ordinal,"</emphasis>\n</para>\n";
1364 else
1366 print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1369 output_api_section_end();
1372 sub output_api_synopsis($)
1374 my $comment = shift;
1375 my @fmt;
1377 output_api_section_start($comment,"SYNOPSIS");
1379 if ($opt_output_format eq "h")
1381 print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1382 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1384 elsif ($opt_output_format eq "s")
1386 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1387 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1389 else
1391 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1392 @fmt = ("", "\n", "\\fI", "\\fR");
1395 # Since our prototype is output in a pre-formatted block, line up the
1396 # parameters and parameter comments in the same column.
1398 # First caluculate where the columns should start
1399 my $biggest_length = 0;
1400 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1402 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1403 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1405 my $length = length $1;
1406 if ($length > $biggest_length)
1408 $biggest_length = $length;
1413 # Now pad the string with blanks
1414 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1416 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1417 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1419 my $pad_len = $biggest_length - length $1;
1420 my $padding = " " x ($pad_len);
1421 ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1425 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1427 # Format the parameter name
1428 my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1429 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1430 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1431 print OUTPUT $line;
1434 if ($opt_output_format eq "h")
1436 print OUTPUT " )\n\n</pre></p>\n";
1438 elsif ($opt_output_format eq "s")
1440 print OUTPUT " )\n</screen>\n";
1442 else
1444 print OUTPUT " )\n";
1447 output_api_section_end();
1450 sub output_api_comment($)
1452 my $comment = shift;
1453 my $open_paragraph = 0;
1454 my $open_raw = 0;
1455 my $param_docs = 0;
1456 my @fmt;
1458 if ($opt_output_format eq "h")
1460 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1461 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1462 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1463 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1464 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1466 elsif ($opt_output_format eq "s")
1468 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1469 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1470 "<screen>\n","</screen>\n",
1471 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1472 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1473 "</entry>","</entry><entry>");
1475 else
1477 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1478 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1481 # Extract the parameter names
1482 my @parameter_names;
1483 for (@{$comment->{PROTOTYPE}})
1485 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1487 push (@parameter_names, $2);
1491 for (@{$comment->{TEXT}})
1493 if ($opt_output_format eq "h" || $opt_output_format eq "s")
1495 # Map special characters
1496 s/\&/\&amp;/g;
1497 s/\</\&lt;/g;
1498 s/\>/\&gt;/g;
1499 s/\([Cc]\)/\&copy;/g;
1500 s/\(tm\)/&#174;/;
1503 if ( s/^\|// )
1505 # Raw output
1506 if ($open_raw == 0)
1508 if ($open_paragraph == 1)
1510 # Close the open paragraph
1511 print OUTPUT $fmt[1];
1512 $open_paragraph = 0;
1514 # Start raw output
1515 print OUTPUT $fmt[12];
1516 $open_raw = 1;
1518 if ($opt_output_format eq "")
1520 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1522 print OUTPUT $_,"\n";
1524 else
1526 if ($opt_output_format eq "h")
1528 # Link to the file in WineHQ cvs
1529 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1531 # Highlight strings
1532 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1533 # Highlight literal chars
1534 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1535 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1536 # Highlight numeric constants
1537 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1539 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1540 # FIXME: Using bullet points for leading '-' would look nicer.
1541 if ($open_paragraph == 1)
1543 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1544 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1546 else
1548 s/^(\-)/$fmt[4]$1$fmt[5]/;
1549 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1552 if ($opt_output_format eq "h")
1554 # Html uses links for API calls
1555 s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1556 # Index references
1557 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1558 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1559 # And references to COM objects (hey, they'll get documented one day)
1560 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1561 # Convert any web addresses to real links
1562 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1564 else
1566 if ($opt_output_format eq "")
1568 # Give the man section for API calls
1569 s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1571 else
1573 # Highlight API calls
1574 s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1577 # And references to COM objects
1578 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1581 if ($open_raw == 1)
1583 # Finish the raw output
1584 print OUTPUT $fmt[13];
1585 $open_raw = 0;
1588 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1590 # Start of a new section
1591 if ($open_paragraph == 1)
1593 if ($param_docs == 1)
1595 print OUTPUT $fmt[17],$fmt[15];
1597 else
1599 print OUTPUT $fmt[1];
1601 $open_paragraph = 0;
1603 output_api_section_start($comment,$_);
1604 if ( /^PARAMS$/ )
1606 print OUTPUT $fmt[14];
1607 $param_docs = 1;
1609 else
1611 #print OUTPUT $fmt[15];
1612 $param_docs = 0;
1615 elsif ( /^$/ )
1617 # Empty line, indicating a new paragraph
1618 if ($open_paragraph == 1)
1620 if ($param_docs == 0)
1622 print OUTPUT $fmt[1];
1623 $open_paragraph = 0;
1627 else
1629 if ($param_docs == 1)
1631 if ($open_paragraph == 1)
1633 # For parameter docs, put each parameter into a new paragraph/table row
1634 print OUTPUT $fmt[17];
1635 $open_paragraph = 0;
1637 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1639 else
1641 # Within paragraph lines, prevent lines running together
1642 $_ = $_." ";
1645 # Format parameter names where they appear in the comment
1646 for my $parameter_name (@parameter_names)
1648 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\=\/])/$1$fmt[8]$2$fmt[9]$3/g;
1650 # Structure dereferences include the dereferenced member
1651 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1652 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1654 if ($open_paragraph == 0)
1656 if ($param_docs == 1)
1658 print OUTPUT $fmt[16];
1660 else
1662 print OUTPUT $fmt[0];
1664 $open_paragraph = 1;
1666 # Anything in all uppercase on its own gets emphasised
1667 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1669 print OUTPUT $_;
1673 if ($open_raw == 1)
1675 print OUTPUT $fmt[13];
1677 if ($open_paragraph == 1)
1679 print OUTPUT $fmt[1];
1683 # Create the master index file
1684 sub output_master_index_files()
1686 if ($opt_output_format eq "")
1688 return; # No master index for man pages
1691 if ($opt_output_format eq "h")
1693 # Append the index entries to the output db of index entries
1694 my $output_file = $opt_output_directory."/index.db";
1695 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1696 for (@index_entries_list)
1698 $_ =~ s/A\,/\,/;
1699 print INDEXDB $_."\n";
1701 close(INDEXDB);
1704 # Use the comment output functions for consistency
1705 my $comment =
1707 FILE => "",
1708 COMMENT_NAME => "The Wine Api Guide",
1709 ALT_NAME => "The Wine Api Guide",
1710 DLL_NAME => "",
1711 ORDINAL => "",
1712 RETURNS => "",
1713 PROTOTYPE => [],
1714 TEXT => [],
1717 if ($opt_output_format eq "s")
1719 $comment->{COMMENT_NAME} = "Introduction";
1720 $comment->{ALT_NAME} = "Introduction",
1722 elsif ($opt_output_format eq "h")
1724 @{$comment->{TEXT}} = (
1725 "NAME",
1726 $comment->{COMMENT_NAME},
1727 "INTRODUCTION",
1731 # Create the initial comment text
1732 push (@{$comment->{TEXT}},
1733 "This document describes the Api calls made available",
1734 "by Wine. They are grouped by the dll that exports them.",
1736 "Please do not edit this document, since it is generated automatically",
1737 "from the Wine source code tree. Details on updating this documentation",
1738 "are given in the \"Wine Developers Guide\".",
1739 "CONTRIBUTORS",
1740 "Api documentation is generally written by the person who ",
1741 "implements a given Api call. Authors of each dll are listed in the overview ",
1742 "section for that dll. Additional contributors who have updated source files ",
1743 "but have not entered their names in a copyright statement are noted by an ",
1744 "entry in the file \"Changelog\" from the Wine source code distribution.",
1748 # Read in all dlls from the database of dll names
1749 my $input_file = $opt_output_directory."/dlls.db";
1750 my @dlls = `cat $input_file|sort|uniq`;
1752 if ($opt_output_format eq "h")
1754 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1755 push (@{$comment->{TEXT}},
1756 "INDEX",
1757 "For an alphabetical listing of the functions available, please click the ",
1758 "first letter of the functions name below:","",
1759 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1760 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1761 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1762 "DLLS",
1763 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1766 # Add the dlls to the comment
1767 for (@dlls)
1769 $_ =~ s/(\..*)?\n/\(\)/;
1770 push (@{$comment->{TEXT}}, $_, "");
1772 output_open_api_file("index");
1774 elsif ($opt_output_format eq "s")
1776 # Just write this as the initial blurb, with a chapter heading
1777 output_open_api_file("blurb");
1778 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1781 # Write out the document
1782 output_api_header($comment);
1783 output_api_comment($comment);
1784 output_api_footer($comment);
1785 if ($opt_output_format eq "s")
1787 print OUTPUT "</chapter>\n" # finish the chapter
1789 output_close_api_file();
1791 if ($opt_output_format eq "s")
1793 output_sgml_master_file(\@dlls);
1794 return;
1796 if ($opt_output_format eq "h")
1798 output_html_index_files();
1799 output_html_stylesheet();
1800 return;
1804 # Write the master wine-api.sgml, linking it to each dll.
1805 sub output_sgml_master_file($)
1807 my $dlls = shift;
1809 output_open_api_file("wine-api");
1810 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1811 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1812 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1814 # List the entities
1815 for (@$dlls)
1817 $_ =~ s/(\..*)?\n//;
1818 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1821 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1822 print OUTPUT " &blurb;\n";
1824 for (@$dlls)
1826 print OUTPUT " &",$_,";\n"
1828 print OUTPUT "\n\n</book>\n";
1830 output_close_api_file();
1833 # Produce the sgml for the dll chapter from the generated files
1834 sub output_sgml_dll_file($)
1836 my $spec_details = shift;
1838 # Make a list of all the documentation files to include
1839 my $exports = $spec_details->{EXPORTS};
1840 my @source_files = ();
1841 for (@$exports)
1843 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1844 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1845 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1847 # A documented function
1848 push (@source_files,@$_[3]);
1852 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1854 @source_files = sort @source_files;
1856 # create a new chapter for this dll
1857 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1858 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1859 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1860 output_close_api_file();
1862 # Add the sorted documentation, cleaning up as we go
1863 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1864 for (@source_files)
1866 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1867 `rm -f $opt_output_directory/$_.sgml`;
1870 # close the chapter, and overwite the dll source
1871 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1872 print OUTPUT "</chapter>\n";
1873 close OUTPUT;
1874 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1877 # Write the html index files containing the function names
1878 sub output_html_index_files()
1880 if ($opt_output_format ne "h")
1882 return;
1885 my @letters = ('_', 'A' .. 'Z');
1887 # Read in all functions
1888 my $input_file = $opt_output_directory."/index.db";
1889 my @funcs = `cat $input_file|sort|uniq`;
1891 for (@letters)
1893 my $letter = $_;
1894 my $comment =
1896 FILE => "",
1897 COMMENT_NAME => "",
1898 ALT_NAME => "",
1899 DLL_NAME => "",
1900 ORDINAL => "",
1901 RETURNS => "",
1902 PROTOTYPE => [],
1903 TEXT => [],
1906 $comment->{COMMENT_NAME} = $letter." Functions";
1907 $comment->{ALT_NAME} = $letter." Functions";
1909 push (@{$comment->{TEXT}},
1910 "NAME",
1911 $comment->{COMMENT_NAME},
1912 "FUNCTIONS"
1915 # Add the functions to the comment
1916 for (@funcs)
1918 my $first_char = substr ($_, 0, 1);
1919 $first_char = uc $first_char;
1921 if ($first_char eq $letter)
1923 my $name = $_;
1924 my $file;
1925 $name =~ s/(^.*?)\,(.*?)\n/$1/;
1926 $file = $2;
1927 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
1931 # Write out the document
1932 output_open_api_file($letter);
1933 output_api_header($comment);
1934 output_api_comment($comment);
1935 output_api_footer($comment);
1936 output_close_api_file();
1940 # Output the stylesheet for HTML output
1941 sub output_html_stylesheet()
1943 if ($opt_output_format ne "h")
1945 return;
1948 my $css;
1949 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
1951 * Default styles for Wine HTML Documentation.
1953 * This style sheet should be altered to suit your needs/taste.
1955 BODY { /* Page body */
1956 background-color: white;
1957 color: black;
1958 font-family: Tahoma,sans-serif;
1959 font-style: normal;
1960 font-size: 10pt;
1962 a:link { color: #4444ff; } /* Links */
1963 a:visited { color: #333377 }
1964 a:active { color: #0000dd }
1965 H2.section { /* Section Headers */
1966 font-family: sans-serif;
1967 color: #777777;
1968 background-color: #F0F0FE;
1969 margin-left: 0.2in;
1970 margin-right: 1.0in;
1972 b.func_name { /* Function Name */
1973 font-size: 10pt;
1974 font-style: bold;
1976 i.dll_ord { /* Italicised DLL+ordinal */
1977 color: #888888;
1978 font-family: sans-serif;
1979 font-size: 8pt;
1981 p { /* Paragraphs */
1982 margin-left: 0.5in;
1983 margin-right: 0.5in;
1985 table { /* tables */
1986 margin-left: 0.5in;
1987 margin-right: 0.5in;
1989 pre.proto /* API Function prototype */
1991 border-style: solid;
1992 border-width: 1px;
1993 border-color: #777777;
1994 background-color: #F0F0BB;
1995 color: black;
1996 font-size: 10pt;
1997 vertical-align: top;
1998 margin-left: 0.5in;
1999 margin-right: 1.0in;
2001 pre.raw { /* Raw text output */
2002 margin-left: 0.6in;
2003 margin-right: 1.1in;
2004 background-color: #8080DC;
2006 tt.param { /* Parameter name */
2007 font-style: italic;
2008 color: blue;
2010 tt.const { /* Constant */
2011 color: red;
2013 i.in_out { /* In/Out */
2014 font-size: 8pt;
2015 color: grey;
2017 tt.coderef { /* Code in description text */
2018 color: darkgreen;
2020 b.emp /* Emphasis */ {
2021 font-style: bold;
2022 color: darkblue;
2024 i.footer { /* Footer */
2025 font-family: sans-serif;
2026 font-size: 6pt;
2027 color: darkgrey;
2029 HERE_TARGET
2031 my $output_file = "$opt_output_directory/apidoc.css";
2032 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2033 print CSS $css;
2034 close(CSS);
2038 sub usage()
2040 print "\nCreate API Documentation from Wine source code.\n\n",
2041 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
2042 "Where: <spec> is a .spec file giving a DLL's exports.\n",
2043 " <include> is an include directory used by the DLL.\n",
2044 " <source> is a source file of the DLL.\n",
2045 " The above can be given multiple times on the command line, as appropriate.\n",
2046 "Options:\n",
2047 " -Th : Output HTML instead of a man page\n",
2048 " -Ts : Output SGML (Docbook source) instead of a man page\n",
2049 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
2050 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
2051 " -e : Output \"FIXME\" documentation from empty comments.\n",
2052 " -v : Verbosity. Can be given more than once for more detail.\n";
2057 # Main
2060 # Print usage if we're called with no args
2061 if( @ARGV == 0)
2063 usage();
2066 # Process command line options
2067 while(defined($_ = shift @ARGV))
2069 if( s/^-// )
2071 # An option.
2072 for ($_)
2074 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
2075 s/^S// && do { $opt_manual_section = $_; last; };
2076 /^Th$/ && do { $opt_output_format = "h"; last; };
2077 /^Ts$/ && do { $opt_output_format = "s"; last; };
2078 /^v$/ && do { $opt_verbose++; last; };
2079 /^e$/ && do { $opt_output_empty = 1; last; };
2080 /^L$/ && do { last; };
2081 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
2082 s/^I// && do { if ($_ ne ".") {
2083 my $include = $_."/*.h";
2084 $include =~ s/\/\//\//g;
2085 my $have_headers = `ls $include >/dev/null 2>&1`;
2086 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
2088 last;
2090 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
2091 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
2092 $opt_wine_root_dir =~ s/\n//;
2093 $opt_wine_root_dir =~ s/\/\//\//g;
2094 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
2095 last;
2097 die "Unrecognised option $_\n";
2100 else
2102 # A source file.
2103 push (@opt_source_file_list, $_);
2107 # Remove duplicate include directories
2108 my %htmp;
2109 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
2111 if ($opt_verbose > 3)
2113 print "Output dir:'".$opt_output_directory."'\n";
2114 print "Section :'".$opt_manual_section."'\n";
2115 print "Format :'".$opt_output_format."'\n";
2116 print "Root :'".$opt_wine_root_dir."'\n";
2117 print "Spec files:'@opt_spec_file_list'\n";
2118 print "Includes :'@opt_header_file_list'\n";
2119 print "Sources :'@opt_source_file_list'\n";
2122 if (@opt_spec_file_list == 0)
2124 exit 0; # Don't bother processing non-dll files
2127 # Read in each .spec files exports and other details
2128 while(my $spec_file = shift @opt_spec_file_list)
2130 process_spec_file($spec_file);
2133 if ($opt_verbose > 3)
2135 foreach my $spec_file ( keys %spec_files )
2137 print "in '$spec_file':\n";
2138 my $spec_details = $spec_files{$spec_file}[0];
2139 my $exports = $spec_details->{EXPORTS};
2140 for (@$exports)
2142 print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n";
2147 # Extract and output the comments from each source file
2148 while(defined($_ = shift @opt_source_file_list))
2150 process_source_file($_);
2153 # Write the index files for each spec
2154 process_index_files();
2156 # Write the master index file
2157 output_master_index_files();
2159 exit 0;