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
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?
30 # Function flags. most of these come from the spec flags
31 my $FLAG_DOCUMENTED = 1;
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
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
54 # All the collected details about all the source files being processed
56 # All documented functions that are to be placed in the index
57 my @index_entries_list = ();
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
($);
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;
97 DLL_NAME
=> $dll_name,
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;
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
136 $flags |= $FLAG_NONAME;
140 $flags |= $FLAG_I386;
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; };
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";
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]);
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;
220 FILE
=> $source_file,
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];
244 s/\n$//; # Strip newline
245 s/^\s+//; # Strip leading space
246 s/\s+$//; # Strip trailing space
249 # Strip multiple tabs & spaces to a single space
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
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;
297 elsif ($parse_state == 1) # Reading in a comment
301 # Found the end of the comment
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 "")
319 push (@
{$comment->{TEXT
}},$last_line);
323 $ignore_blank_lines = 1;
327 $ignore_blank_lines = 0;
331 if ($ignore_blank_lines == 0 || $_ ne "")
333 push (@
{$comment->{TEXT
}},$_); # Add the comment text
338 # This isn't a well formatted comment: look for the next one
342 elsif ($parse_state == 2) # Finished reading in a comment
344 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
347 # Comment is followed by a function definition
348 $parse_state = 4; # Fall through to read prototype
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";
369 elsif ($parse_state == 3) # Reading in the first line of a comment
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
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
} = "";
392 # This isn't a well formatted comment: look for the next one
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 "")
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.");
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);
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";
447 # Standardise a comments text for consistency
448 sub process_comment_text
($)
453 for (@
{$comment->{TEXT
}})
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
463 s/False|false/FALSE/g;
465 # Preferred capitalisations
466 s/ wine| WINE/ Wine/g;
467 s/ API | api / Api /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;
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
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'
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;
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;
498 s/( bool )/ boolean /g;
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].".";
520 # Standardise our comment and output it if it is suitable.
521 sub process_comment
($)
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";
538 if ($comment->{COMMENT_NAME
} eq "@")
542 # Find the name from the .spec file
543 for (@
{$spec_details->{EXPORTS
}})
545 if (@
$_[0] eq $comment->{ORDINAL
})
547 $comment->{COMMENT_NAME
} = @
$_[2];
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";
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
}})
690 elsif ( /^DESCRIPTION$/ )
694 elsif ($in_description == 1)
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";
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;
734 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
736 @
$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
739 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
741 @
$export[4] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
744 $found_description_text = 1;
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;
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;
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
794 if (@
$export[4] & $FLAG_NONAME)
796 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
800 if ($comment->{COMMENT_NAME
} ne "")
802 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
804 my $exit_value = $?
>> 8;
805 if ($exit_value == 0)
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";
818 my $exit_value = $?
>> 8;
819 if ($exit_value == 0)
824 $h_file = `basename $tmp`;
832 $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine."
836 $h_file = "Defined in \"".$h_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
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
($)
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";
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
}}, $_);
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);
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";
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
},
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
($)
986 if ($opt_verbose > 0)
988 print "Processing ",$comment->{COMMENT_NAME
},"\n";
991 if ($opt_verbose > 4)
994 for (@
{$comment->{PROTOTYPE
}})
999 print "--COMMENT--\n";
1000 for (@
{$comment->{TEXT
} })
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
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
1048 FILE
=> $spec_details->{DLL_NAME
},
1049 COMMENT_NAME
=> $spec_details->{DLL_NAME
}.".dll",
1050 ALT_NAME
=> $spec_details->{DLL_NAME
},
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.
1078 for (@
{$spec_details->{SOURCES
}})
1080 my $source_details = $source_files{$_}[0];
1081 for (@
{$source_details->{CONTRIBUTORS
}})
1083 push (@contributors, $_);
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];
1100 @contributors = grep(!$saw{$_}++, @contributors);
1102 if ($opt_verbose > 3)
1104 print "Contributors:\n";
1110 my $contribstring = join (", ", @contributors);
1112 # Create the initial comment text
1113 @
{$comment->{TEXT
}} = (
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
}},
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."%)",
1139 "The following people hold copyrights on the source files comprising this dll:",
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
};
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)";
1187 if (@
$_[4] & $FLAG_DOCUMENTED)
1190 $line = @
$_[2]." (implemented as ".@
$_[3]."())";
1191 if (@
$_[2] ne @
$_[3])
1193 $line = @
$_[2]." (implemented as ".@
$_[3]."())";
1197 $line = @
$_[2]."()";
1199 if (!(@
$_[4] & $FLAG_WPAIR))
1201 # This function should be indexed
1202 push (@index_entries_list, @
$_[2].",".@
$_[3]);
1207 $line = @
$_[2]." (not documented)";
1212 push (@
{$comment->{TEXT
}}, $line, "");
1216 # Add links to the extra documentation
1217 if (@
{$spec_details->{EXTRA_COMMENTS
}})
1219 push (@
{$comment->{TEXT
}}, "SEE ALSO");
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";
1246 if ($opt_output_format eq "s")
1248 output_sgml_dll_file
($spec_details);
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.
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";
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
()
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",
1306 "<title>$comment->{COMMENT_NAME}</title>\n";
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 © ".$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";
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";
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> <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";
1383 print OUTPUT
"\\fB",$comment->{COMMENT_NAME
},"\\fR ",$dll_ordinal;
1386 output_api_section_end
();
1389 sub output_api_synopsis
($)
1391 my $comment = shift;
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>");
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]/;
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";
1461 print OUTPUT
" )\n";
1464 output_api_section_end
();
1467 sub output_api_comment
($)
1469 my $comment = shift;
1470 my $open_paragraph = 0;
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>");
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
1516 s/\([Cc]\)/\©/g;
1525 if ($open_paragraph == 1)
1527 # Close the open paragraph
1528 print OUTPUT
$fmt[1];
1529 $open_paragraph = 0;
1532 print OUTPUT
$fmt[12];
1535 if ($opt_output_format eq "")
1537 print OUTPUT
".br\n"; # Prevent 'man' running these lines together
1539 print OUTPUT
$_,"\n";
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;
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]/;
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
;
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
;
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;
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;
1600 # Finish the raw output
1601 print OUTPUT
$fmt[13];
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];
1616 print OUTPUT
$fmt[1];
1618 $open_paragraph = 0;
1620 output_api_section_start
($comment,$_);
1623 print OUTPUT
$fmt[14];
1628 #print OUTPUT $fmt[15];
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;
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
1658 # Within paragraph lines, prevent lines running together
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/(\-\>\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1671 if ($open_paragraph == 0)
1673 if ($param_docs == 1)
1675 print OUTPUT
$fmt[16];
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;
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)
1716 print INDEXDB
$_."\n";
1721 # Use the comment output functions for consistency
1725 COMMENT_NAME
=> "The Wine Api Guide",
1726 ALT_NAME
=> "The Wine Api Guide",
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
}} = (
1743 $comment->{COMMENT_NAME
},
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\".",
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
}},
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() ]", "",
1780 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1783 # Add the dlls to the comment
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);
1813 if ($opt_output_format eq "h")
1815 output_html_index_files
();
1816 output_html_stylesheet
();
1821 # Write the master wine-api.sgml, linking it to each dll.
1822 sub output_sgml_master_file
($)
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";
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";
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 = ();
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`;
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";
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")
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`;
1923 $comment->{COMMENT_NAME
} = $letter." Functions";
1924 $comment->{ALT_NAME
} = $letter." Functions";
1926 push (@
{$comment->{TEXT
}},
1928 $comment->{COMMENT_NAME
},
1932 # Add the functions to the comment
1935 my $first_char = substr ($_, 0, 1);
1936 $first_char = uc $first_char;
1938 if ($first_char eq $letter)
1942 $name =~ s/(^.*?)\,(.*?)\n/$1/;
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")
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;
1975 font-family: Tahoma,sans-serif;
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;
1985 background-color: #F0F0FE;
1987 margin-right: 1.0in;
1989 b.func_name { /* Function Name */
1993 i.dll_ord { /* Italicised DLL+ordinal */
1995 font-family: sans-serif;
1998 p { /* Paragraphs */
2000 margin-right: 0.5in;
2002 table { /* tables */
2004 margin-right: 0.5in;
2006 pre.proto /* API Function prototype */
2008 border-style: solid;
2010 border-color: #777777;
2011 background-color: #F0F0BB;
2014 vertical-align: top;
2016 margin-right: 1.0in;
2018 pre.raw { /* Raw text output */
2020 margin-right: 1.1in;
2021 background-color: #8080DC;
2023 tt.param { /* Parameter name */
2027 tt.const { /* Constant */
2030 i.in_out { /* In/Out */
2034 tt.coderef { /* Code in description text */
2037 b.emp /* Emphasis */ {
2041 i.footer { /* Footer */
2042 font-family: sans-serif;
2048 my $output_file = "$opt_output_directory/apidoc.css";
2049 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
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",
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";
2077 # Print usage if we're called with no args
2083 # Process command line options
2084 while(defined($_ = shift @ARGV))
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); }
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."/"; };
2114 die "Unrecognised option $_\n";
2120 push (@opt_source_file_list, $_);
2124 # Remove duplicate include directories
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};
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();