| #! /usr/bin/perl -w |
| # |
| # Generate API documentation. See documentation/documentation.sgml for details. |
| # |
| # Copyright (C) 2000 Mike McCormack |
| # Copyright (C) 2003 Jon Griffiths |
| # |
| # This library is free software; you can redistribute it and/or |
| # modify it under the terms of the GNU Lesser General Public |
| # License as published by the Free Software Foundation; either |
| # version 2.1 of the License, or (at your option) any later version. |
| # |
| # This library is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| # Lesser General Public License for more details. |
| # |
| # You should have received a copy of the GNU Lesser General Public |
| # License along with this library; if not, write to the Free Software |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| # |
| # TODO |
| # Consolidate A+W pairs together, and only write one doc, without the suffix |
| # Implement automatic docs fo structs/defines in headers |
| # SGML gurus - feel free to smarten up the SGML. |
| # Add any other relevant information for the dll - imports etc |
| # Should we have a special output mode for WineHQ? |
| |
| use strict; |
| use bytes; |
| |
| # Function flags. most of these come from the spec flags |
| my $FLAG_DOCUMENTED = 1; |
| my $FLAG_NONAME = 2; |
| my $FLAG_I386 = 4; |
| my $FLAG_REGISTER = 8; |
| my $FLAG_APAIR = 16; # The A version of a matching W function |
| my $FLAG_WPAIR = 32; # The W version of a matching A function |
| my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function |
| |
| |
| # Options |
| my $opt_output_directory = "man3w"; # All default options are for nroff (man pages) |
| my $opt_manual_section = "3w"; |
| my $opt_wine_root_dir = ""; |
| my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml |
| my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function) |
| my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section |
| my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging) |
| my @opt_header_file_list = (); |
| my @opt_spec_file_list = (); |
| my @opt_source_file_list = (); |
| |
| # All the collected details about all the .spec files being processed |
| my %spec_files; |
| # All the collected details about all the source files being processed |
| my %source_files; |
| # All documented functions that are to be placed in the index |
| my @index_entries_list = (); |
| |
| # useful globals |
| my $pwd = `pwd`."/"; |
| $pwd =~ s/\n//; |
| my @datetime = localtime; |
| my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ); |
| my $year = $datetime[5] + 1900; |
| my $date = "$months[$datetime[4]] $year"; |
| |
| |
| sub output_api_comment($); |
| sub output_api_footer($); |
| sub output_api_header($); |
| sub output_api_name($); |
| sub output_api_synopsis($); |
| sub output_close_api_file(); |
| sub output_comment($); |
| sub output_html_index_files(); |
| sub output_html_stylesheet(); |
| sub output_open_api_file($); |
| sub output_sgml_dll_file($); |
| sub output_sgml_master_file($); |
| sub output_spec($); |
| sub process_comment($); |
| sub process_extra_comment($); |
| |
| |
| # Generate the list of exported entries for the dll |
| sub process_spec_file($) |
| { |
| my $spec_name = shift; |
| my $dll_name = $spec_name; |
| $dll_name =~ s/\..*//; # Strip the file extension |
| my $uc_dll_name = uc $dll_name; |
| |
| my $spec_details = |
| { |
| NAME => $spec_name, |
| DLL_NAME => $dll_name, |
| NUM_EXPORTS => 0, |
| NUM_STUBS => 0, |
| NUM_FUNCS => 0, |
| NUM_FORWARDS => 0, |
| NUM_VARS => 0, |
| NUM_DOCS => 0, |
| CONTRIBUTORS => [ ], |
| SOURCES => [ ], |
| DESCRIPTION => [ ], |
| EXPORTS => [ ], |
| EXPORTED_NAMES => { }, |
| IMPLEMENTATION_NAMES => { }, |
| EXTRA_COMMENTS => [ ], |
| CURRENT_EXTRA => [ ] , |
| }; |
| |
| if ($opt_verbose > 0) |
| { |
| print "Processing ".$spec_name."\n"; |
| } |
| |
| # We allow opening to fail just to cater for the peculiarities of |
| # the Wine build system. This doesn't hurt, in any case |
| open(SPEC_FILE, "<$spec_name") || return; |
| |
| while(<SPEC_FILE>) |
| { |
| s/^\s+//; # Strip leading space |
| s/\s+\n$/\n/; # Strip trailing space |
| s/\s+/ /g; # Strip multiple tabs & spaces to a single space |
| s/\s*#.*//; # Strip comments |
| s/\(.*\)/ /; # Strip arguments |
| s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again) |
| s/\n$//; # Strip newline |
| |
| my $flags = 0; |
| if( /\-noname/ ) |
| { |
| $flags |= $FLAG_NONAME; |
| } |
| if( /\-i386/ ) |
| { |
| $flags |= $FLAG_I386; |
| } |
| if( /\-register/ ) |
| { |
| $flags |= $FLAG_REGISTER; |
| } |
| s/ \-[a-z0-9]+//g; # Strip flags |
| |
| if( /^(([0-9]+)|@) / ) |
| { |
| # This line contains an exported symbol |
| my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' '); |
| |
| for ($call_convention) |
| { |
| /^(cdecl|stdcall|varargs|pascal)$/ |
| && do { $spec_details->{NUM_FUNCS}++; last; }; |
| /^(variable|equate)$/ |
| && do { $spec_details->{NUM_VARS}++; last; }; |
| /^(extern)$/ |
| && do { $spec_details->{NUM_FORWARDS}++; last; }; |
| /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; }; |
| if ($opt_verbose > 0) |
| { |
| print "Warning: didn't recognise convention \'",$call_convention,"'\n"; |
| } |
| last; |
| } |
| |
| # Convert ordinal only names so we can find them later |
| if ($exported_name eq "@") |
| { |
| $exported_name = $uc_dll_name.'_'.$ordinal; |
| } |
| if (!defined($implementation_name)) |
| { |
| $implementation_name = $exported_name; |
| } |
| if ($implementation_name eq "") |
| { |
| $implementation_name = $exported_name; |
| } |
| |
| if ($implementation_name =~ /(.*?)\./) |
| { |
| $call_convention = "forward"; # Referencing a function from another dll |
| $spec_details->{NUM_FUNCS}--; |
| $spec_details->{NUM_FORWARDS}++; |
| } |
| |
| # Add indices for the exported and implementation names |
| $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS}; |
| if ($implementation_name ne $exported_name) |
| { |
| $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS}; |
| } |
| |
| # Add the exported entry |
| $spec_details->{NUM_EXPORTS}++; |
| my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags); |
| push (@{$spec_details->{EXPORTS}},[@export]); |
| } |
| } |
| close(SPEC_FILE); |
| |
| # Add this .spec files details to the list of .spec files |
| $spec_files{$uc_dll_name} = [$spec_details]; |
| } |
| |
| # Read each source file, extract comments, and generate API documentation if appropriate |
| sub process_source_file($) |
| { |
| my $source_file = shift; |
| my $source_details = |
| { |
| CONTRIBUTORS => [ ], |
| DEBUG_CHANNEL => "", |
| }; |
| my $comment = |
| { |
| FILE => $source_file, |
| COMMENT_NAME => "", |
| ALT_NAME => "", |
| DLL_NAME => "", |
| ORDINAL => "", |
| RETURNS => "", |
| PROTOTYPE => [], |
| TEXT => [], |
| }; |
| my $parse_state = 0; |
| my $ignore_blank_lines = 1; |
| my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export |
| |
| if ($opt_verbose > 0) |
| { |
| print "Processing ".$source_file."\n"; |
| } |
| open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n"; |
| |
| # Add this source file to the list of source files |
| $source_files{$source_file} = [$source_details]; |
| |
| while(<SOURCE_FILE>) |
| { |
| s/\n$//; # Strip newline |
| s/^\s+//; # Strip leading space |
| s/\s+$//; # Strip trailing space |
| if (! /^\*\|/ ) |
| { |
| # Strip multiple tabs & spaces to a single space |
| s/\s+/ /g; |
| } |
| |
| if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ ) |
| { |
| # Extract a contributor to this file |
| my $contributor = $2; |
| $contributor =~ s/ *$//; |
| $contributor =~ s/^by //; |
| $contributor =~ s/\.$//; |
| $contributor =~ s/ (for .*)/ \($1\)/; |
| if ($contributor ne "") |
| { |
| if ($opt_verbose > 3) |
| { |
| print "Info: Found contributor:'".$contributor."'\n"; |
| } |
| push (@{$source_details->{CONTRIBUTORS}},$contributor); |
| } |
| } |
| elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ ) |
| { |
| # Extract the debug channel to use |
| if ($opt_verbose > 3) |
| { |
| print "Info: Found debug channel:'".$1."'\n"; |
| } |
| $source_details->{DEBUG_CHANNEL} = $1; |
| } |
| |
| if ($parse_state == 0) # Searching for a comment |
| { |
| if ( /^\/\**$/ ) |
| { |
| # Found a comment start |
| $comment->{COMMENT_NAME} = ""; |
| $comment->{ALT_NAME} = ""; |
| $comment->{DLL_NAME} = ""; |
| $comment->{ORDINAL} = ""; |
| $comment->{RETURNS} = ""; |
| $comment->{PROTOTYPE} = []; |
| $comment->{TEXT} = []; |
| $ignore_blank_lines = 1; |
| $extra_comment = 0; |
| $parse_state = 3; |
| } |
| } |
| elsif ($parse_state == 1) # Reading in a comment |
| { |
| if ( /^\**\// ) |
| { |
| # Found the end of the comment |
| $parse_state = 2; |
| } |
| elsif ( s/^\*\|/\|/ ) |
| { |
| # A line of comment not meant to be pre-processed |
| push (@{$comment->{TEXT}},$_); # Add the comment text |
| } |
| elsif ( s/^ *\** *// ) |
| { |
| # A line of comment, starting with an asterisk |
| if ( /^[A-Z]+$/ || $_ eq "") |
| { |
| # This is a section start, so skip blank lines before and after it. |
| my $last_line = pop(@{$comment->{TEXT}}); |
| if (defined($last_line) && $last_line ne "") |
| { |
| # Put it back |
| push (@{$comment->{TEXT}},$last_line); |
| } |
| if ( /^[A-Z]+$/ ) |
| { |
| $ignore_blank_lines = 1; |
| } |
| else |
| { |
| $ignore_blank_lines = 0; |
| } |
| } |
| |
| if ($ignore_blank_lines == 0 || $_ ne "") |
| { |
| push (@{$comment->{TEXT}},$_); # Add the comment text |
| } |
| } |
| else |
| { |
| # This isn't a well formatted comment: look for the next one |
| $parse_state = 0; |
| } |
| } |
| elsif ($parse_state == 2) # Finished reading in a comment |
| { |
| if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ || |
| /.*?\(/ ) |
| { |
| # Comment is followed by a function definition |
| $parse_state = 4; # Fall through to read prototype |
| } |
| else |
| { |
| # Allow cpp directives and blank lines between the comment and prototype |
| if ($extra_comment == 1) |
| { |
| # An extra comment not followed by a function definition |
| $parse_state = 5; # Fall through to process comment |
| } |
| elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/) |
| { |
| # This isn't a well formatted comment: look for the next one |
| if ($opt_verbose > 1) |
| { |
| print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n"; |
| } |
| $parse_state = 0; |
| } |
| } |
| } |
| elsif ($parse_state == 3) # Reading in the first line of a comment |
| { |
| s/^ *\** *//; |
| if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ ) |
| { |
| # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line. |
| if (defined ($7) && $7 ne "") |
| { |
| push (@{$comment->{TEXT}},$_); # Add the trailing comment text |
| } |
| $comment->{COMMENT_NAME} = $1; |
| $comment->{DLL_NAME} = uc $3; |
| $comment->{ORDINAL} = $4; |
| $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code |
| $parse_state = 1; |
| } |
| elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ ) |
| { |
| # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation) |
| $comment->{COMMENT_NAME} = $1; |
| $comment->{DLL_NAME} = uc $2; |
| $comment->{ORDINAL} = ""; |
| $extra_comment = 1; |
| $parse_state = 1; |
| } |
| else |
| { |
| # This isn't a well formatted comment: look for the next one |
| $parse_state = 0; |
| } |
| } |
| |
| if ($parse_state == 4) # Reading in the function definition |
| { |
| push (@{$comment->{PROTOTYPE}},$_); |
| # Strip comments from the line before checking for ')' |
| my $stripped_line = $_; |
| $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//; |
| if ( $stripped_line =~ /\)/ ) |
| { |
| # Strip a blank last line |
| my $last_line = pop(@{$comment->{TEXT}}); |
| if (defined($last_line) && $last_line ne "") |
| { |
| # Put it back |
| push (@{$comment->{TEXT}},$last_line); |
| } |
| |
| if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0) |
| { |
| # Create a 'not implemented' comment |
| @{$comment->{TEXT}} = ("fixme: This function has not yet been documented."); |
| } |
| $parse_state = 5; |
| } |
| } |
| |
| if ($parse_state == 5) # Processing the comment |
| { |
| # Process it, if it has any text |
| if (@{$comment->{TEXT}} > 0) |
| { |
| if ($extra_comment == 1) |
| { |
| process_extra_comment($comment); |
| } |
| else |
| { |
| @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}}); |
| process_comment($comment); |
| } |
| } |
| elsif ($opt_verbose > 1 && $opt_output_empty == 0) |
| { |
| print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n"; |
| } |
| $parse_state = 0; |
| } |
| } |
| close(SOURCE_FILE); |
| } |
| |
| # Standardise a comments text for consistency |
| sub process_comment_text($) |
| { |
| my $comment = shift; |
| my $in_params = 0; |
| my @tmp_list = (); |
| my $i = 0; |
| |
| for (@{$comment->{TEXT}}) |
| { |
| my $line = $_; |
| |
| if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ ) |
| { |
| $in_params = 0; |
| } |
| if ( $in_params > 0 && !/\[/ && !/\]/ ) |
| { |
| # Possibly a continuation of the parameter description |
| my $last_line = pop(@tmp_list); |
| if ( $last_line =~ /\[/ && $last_line =~ /\]/ ) |
| { |
| $line = $last_line." ".$_; |
| } |
| else |
| { |
| $in_params = 0; |
| push (@tmp_list, $last_line); |
| } |
| } |
| if ( /^(PARAMS|MEMBERS)$/ ) |
| { |
| $in_params = 1; |
| } |
| push (@tmp_list, $line); |
| } |
| |
| @{$comment->{TEXT}} = @tmp_list; |
| |
| for (@{$comment->{TEXT}}) |
| { |
| if (! /^\|/ ) |
| { |
| # Map I/O values. These come in too many formats to standardise now.... |
| s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g; |
| s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g; |
| s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g; |
| # TRUE/FALSE/NULL are defines, capitilise them |
| s/True|true/TRUE/g; |
| s/False|false/FALSE/g; |
| s/Null|null/NULL/g; |
| # Preferred capitalisations |
| s/ wine| WINE/ Wine/g; |
| s/ API | api / Api /g; |
| s/DLL|Dll/dll /g; |
| s/ URL | url / Url /g; |
| s/WIN16|win16/Win16/g; |
| s/WIN32|win32/Win32/g; |
| s/WIN64|win64/Win64/g; |
| s/ ID | id / Id /g; |
| # Grammar |
| s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop |
| s/ \:/\:/g; # Colons to the left |
| s/ \;/\;/g; # Semi-colons too |
| # Common idioms |
| s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W |
| s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto |
| s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64 |
| s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS' |
| # Trademarks |
| s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g; |
| s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g; |
| s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g; |
| s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g; |
| s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g; |
| # Abbreviations |
| s/( char )/ character /g; |
| s/( chars )/ characters /g; |
| s/( info )/ information /g; |
| s/( app )/ application /g; |
| s/( apps )/ applications /g; |
| s/( exe )/ executable /g; |
| s/( ptr )/ pointer /g; |
| s/( obj )/ object /g; |
| s/( err )/ error /g; |
| s/( bool )/ boolean /g; |
| s/( no\. )/ number /g; |
| s/( No\. )/ Number /g; |
| # Punctuation |
| if ( /\[I|\[O/ && ! /\.$/ ) |
| { |
| $_ = $_."."; # Always have a full stop at the end of parameter desc. |
| } |
| elsif ($i > 0 && /^[A-Z]*$/ && |
| !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) && |
| !(@{$comment->{TEXT}}[$i-1] =~ /\:$/)) |
| { |
| |
| if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/)) |
| { |
| # Paragraphs always end with a full stop |
| @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1]."."; |
| } |
| } |
| } |
| $i++; |
| } |
| } |
| |
| # Standardise our comment and output it if it is suitable. |
| sub process_comment($) |
| { |
| my $comment = shift; |
| |
| # Don't process this comment if the function isn't exported |
| my $spec_details = $spec_files{$comment->{DLL_NAME}}[0]; |
| |
| if (!defined($spec_details)) |
| { |
| if ($opt_verbose > 2) |
| { |
| print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '". |
| $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n"; |
| } |
| return; |
| } |
| |
| if ($comment->{COMMENT_NAME} eq "@") |
| { |
| my $found = 0; |
| |
| # Find the name from the .spec file |
| for (@{$spec_details->{EXPORTS}}) |
| { |
| if (@$_[0] eq $comment->{ORDINAL}) |
| { |
| $comment->{COMMENT_NAME} = @$_[2]; |
| $found = 1; |
| } |
| } |
| |
| if ($found == 0) |
| { |
| # Create an implementation name |
| $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL}; |
| } |
| } |
| |
| my $exported_names = $spec_details->{EXPORTED_NAMES}; |
| my $export_index = $exported_names->{$comment->{COMMENT_NAME}}; |
| my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES}; |
| |
| if (!defined($export_index)) |
| { |
| # Perhaps the comment uses the implementation name? |
| $export_index = $implementation_names->{$comment->{COMMENT_NAME}}; |
| } |
| if (!defined($export_index)) |
| { |
| # This function doesn't appear to be exported. hmm. |
| if ($opt_verbose > 2) |
| { |
| print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '". |
| $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n"; |
| } |
| return; |
| } |
| |
| # When the function is exported twice we have the second name below the first |
| # (you see this a lot in ntdll, but also in some other places). |
| my $first_line = ${@{$comment->{TEXT}}}[1]; |
| |
| if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ ) |
| { |
| # Found a second name - mark it as documented |
| my $alt_index = $exported_names->{$1}; |
| if (defined($alt_index)) |
| { |
| if ($opt_verbose > 2) |
| { |
| print "Info: Found alternate name '",$1,"\n"; |
| } |
| my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index]; |
| @$alt_export[4] |= $FLAG_DOCUMENTED; |
| $spec_details->{NUM_DOCS}++; |
| ${@{$comment->{TEXT}}}[1] = ""; |
| } |
| } |
| |
| if (@{$spec_details->{CURRENT_EXTRA}}) |
| { |
| # We have an extra comment that might be related to this one |
| my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0]; |
| my $current_name = $current_comment->{COMMENT_NAME}; |
| if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name) |
| { |
| if ($opt_verbose > 2) |
| { |
| print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n"; |
| } |
| # Add a reference to this comment to our extra comment |
| push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()",""); |
| } |
| } |
| |
| # We want our docs generated using the implementation name, so they are unique |
| my $export = @{$spec_details->{EXPORTS}}[$export_index]; |
| $comment->{COMMENT_NAME} = @$export[3]; |
| $comment->{ALT_NAME} = @$export[2]; |
| |
| # Mark the function as documented |
| $spec_details->{NUM_DOCS}++; |
| @$export[4] |= $FLAG_DOCUMENTED; |
| |
| # This file is used by the DLL - Make sure we get our contributors right |
| push (@{$spec_details->{SOURCES}},$comment->{FILE}); |
| |
| # If we have parameter comments in the prototype, extract them |
| my @parameter_comments; |
| for (@{$comment->{PROTOTYPE}}) |
| { |
| s/ *\, */\,/g; # Strip spaces from around commas |
| |
| if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment |
| { |
| my $parameter_comment = $2; |
| if (!$parameter_comment =~ /^\[/ ) |
| { |
| # Add [IO] markers so we format the comment correctly |
| $parameter_comment = "[fixme] ".$parameter_comment; |
| } |
| if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ ) |
| { |
| # Add the parameter name |
| $parameter_comment = $2." ".$parameter_comment; |
| } |
| push (@parameter_comments, $parameter_comment); |
| } |
| } |
| |
| # If we extracted any prototype comments, add them to the comment text. |
| if (@parameter_comments) |
| { |
| @parameter_comments = ("PARAMS", @parameter_comments); |
| my @new_comment = (); |
| my $inserted_params = 0; |
| |
| for (@{$comment->{TEXT}}) |
| { |
| if ( $inserted_params == 0 && /^[A-Z]+$/ ) |
| { |
| # Found a section header, so this is where we insert |
| push (@new_comment, @parameter_comments); |
| $inserted_params = 1; |
| } |
| push (@new_comment, $_); |
| } |
| if ($inserted_params == 0) |
| { |
| # Add them to the end |
| push (@new_comment, @parameter_comments); |
| } |
| $comment->{TEXT} = [@new_comment]; |
| } |
| |
| if ($opt_fussy == 1 && $opt_output_empty == 0) |
| { |
| # Reject any comment that doesn't have a description or a RETURNS section. |
| # This is the default for now, 'coz many comments aren't suitable. |
| my $found_returns = 0; |
| my $found_description_text = 0; |
| my $in_description = 0; |
| for (@{$comment->{TEXT}}) |
| { |
| if ( /^RETURNS$/ ) |
| { |
| $found_returns = 1; |
| $in_description = 0; |
| } |
| elsif ( /^DESCRIPTION$/ ) |
| { |
| $in_description = 1; |
| } |
| elsif ($in_description == 1) |
| { |
| if ( !/^[A-Z]+$/ ) |
| { |
| # Don't reject comments that refer to another doc (e.g. A/W) |
| if ( /^See ([A-Za-z0-9_]+)\.$/ ) |
| { |
| if ($comment->{COMMENT_NAME} =~ /W$/ ) |
| { |
| # This is probably a Unicode version of an Ascii function. |
| # Create the Ascii name and see if its been documented |
| my $ascii_name = $comment->{COMMENT_NAME}; |
| $ascii_name =~ s/W$/A/; |
| |
| my $ascii_export_index = $exported_names->{$ascii_name}; |
| |
| if (!defined($ascii_export_index)) |
| { |
| $ascii_export_index = $implementation_names->{$ascii_name}; |
| } |
| if (!defined($ascii_export_index)) |
| { |
| if ($opt_verbose > 2) |
| { |
| print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n"; |
| } |
| } |
| else |
| { |
| my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index]; |
| if (@$ascii_export[4] & $FLAG_DOCUMENTED) |
| { |
| # Flag these functions as an A/W pair |
| @$ascii_export[4] |= $FLAG_APAIR; |
| @$export[4] |= $FLAG_WPAIR; |
| } |
| } |
| } |
| $found_returns = 1; |
| } |
| elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ ) |
| { |
| @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version |
| $found_returns = 1; |
| } |
| elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ ) |
| { |
| @$export[4] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version |
| $found_returns = 1; |
| } |
| $found_description_text = 1; |
| } |
| else |
| { |
| $in_description = 0; |
| } |
| } |
| } |
| if ($found_returns == 0 || $found_description_text == 0) |
| { |
| if ($opt_verbose > 2) |
| { |
| print "Info: Function '",$comment->{COMMENT_NAME},"' has no ", |
| "description and/or RETURNS section, skipping\n"; |
| } |
| $spec_details->{NUM_DOCS}--; |
| @$export[4] &= ~$FLAG_DOCUMENTED; |
| return; |
| } |
| } |
| |
| process_comment_text($comment); |
| |
| # Strip the prototypes return value, call convention, name and brackets |
| # (This leaves it as a list of types and names, or empty for void functions) |
| my $prototype = join(" ", @{$comment->{PROTOTYPE}}); |
| $prototype =~ s/ / /g; |
| |
| if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ) |
| { |
| $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/; |
| $comment->{RETURNS} = $1; |
| } |
| else |
| { |
| $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\( *(.*)/$3/; |
| $comment->{RETURNS} = $1; |
| } |
| |
| $prototype =~ s/ *\).*//; # Strip end bracket |
| $prototype =~ s/ *\* */\*/g; # Strip space around pointers |
| $prototype =~ s/ *\, */\,/g; # Strip space around commas |
| $prototype =~ s/^(void|VOID)$//; # If void, leave blank |
| $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name |
| @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype); |
| |
| # FIXME: If we have no parameters, make sure we have a PARAMS: None. section |
| |
| # Find header file |
| my $h_file = ""; |
| if (@$export[4] & $FLAG_NONAME) |
| { |
| $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function."; |
| } |
| else |
| { |
| if ($comment->{COMMENT_NAME} ne "") |
| { |
| my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null"; |
| $tmp = `$tmp`; |
| my $exit_value = $? >> 8; |
| if ($exit_value == 0) |
| { |
| $tmp =~ s/\n.*//g; |
| if ($tmp ne "") |
| { |
| $h_file = `basename $tmp`; |
| } |
| } |
| } |
| elsif ($comment->{ALT_NAME} ne "") |
| { |
| my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null"; |
| $tmp = `$tmp`; |
| my $exit_value = $? >> 8; |
| if ($exit_value == 0) |
| { |
| $tmp =~ s/\n.*//g; |
| if ($tmp ne "") |
| { |
| $h_file = `basename $tmp`; |
| } |
| } |
| } |
| $h_file =~ s/^ *//; |
| $h_file =~ s/\n//; |
| if ($h_file eq "") |
| { |
| $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine." |
| } |
| else |
| { |
| $h_file = "Defined in \"".$h_file."\"."; |
| } |
| } |
| |
| # Find source file |
| my $c_file = $comment->{FILE}; |
| if ($opt_wine_root_dir ne "") |
| { |
| my $cfile = $pwd."/".$c_file; # Current dir + file |
| $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename |
| $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../") |
| $cfile =~ s/\n//; # Strip newline |
| my $newfile = $c_file; |
| $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename |
| $cfile = $cfile."/".$newfile; # Append filename to base path |
| $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory |
| $cfile =~ s/\/\//\//g; # Remove any double slashes |
| $cfile =~ s/^\/+//; # Strip initial directory slash |
| $c_file = $cfile; |
| } |
| $c_file = "Implemented in \"".$c_file."\"."; |
| |
| # Add the implementation details |
| push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file); |
| |
| if (@$export[4] & $FLAG_I386) |
| { |
| push (@{$comment->{TEXT}}, "", "Available on x86 platforms only."); |
| } |
| if (@$export[4] & $FLAG_REGISTER) |
| { |
| push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ", |
| "For more details, please read the source code."); |
| } |
| my $source_details = $source_files{$comment->{FILE}}[0]; |
| if ($source_details->{DEBUG_CHANNEL} ne "") |
| { |
| push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\"."); |
| } |
| |
| # Write out the documentation for the API |
| output_comment($comment) |
| } |
| |
| # process our extra comment and output it if it is suitable. |
| sub process_extra_comment($) |
| { |
| my $comment = shift; |
| |
| my $spec_details = $spec_files{$comment->{DLL_NAME}}[0]; |
| |
| if (!defined($spec_details)) |
| { |
| if ($opt_verbose > 2) |
| { |
| print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '". |
| $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n"; |
| } |
| return; |
| } |
| |
| # Check first to see if this is documentation for the DLL. |
| if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME}) |
| { |
| if ($opt_verbose > 2) |
| { |
| print "Info: Found DLL documentation\n"; |
| } |
| for (@{$comment->{TEXT}}) |
| { |
| push (@{$spec_details->{DESCRIPTION}}, $_); |
| } |
| return; |
| } |
| |
| # Add the comment to the DLL page as a link |
| push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME}); |
| |
| # If we have a prototype, process as a regular comment |
| if (@{$comment->{PROTOTYPE}}) |
| { |
| $comment->{ORDINAL} = "@"; |
| |
| # Add an index for the comment name |
| $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS}; |
| |
| # Add a fake exported entry |
| $spec_details->{NUM_EXPORTS}++; |
| my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) = |
| ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0); |
| my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented); |
| push (@{$spec_details->{EXPORTS}},[@export]); |
| @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}}); |
| process_comment($comment); |
| return; |
| } |
| |
| if ($opt_verbose > 0) |
| { |
| print "Processing ",$comment->{COMMENT_NAME},"\n"; |
| } |
| |
| if (@{$spec_details->{CURRENT_EXTRA}}) |
| { |
| my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0]; |
| |
| if ($opt_verbose > 0) |
| { |
| print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n"; |
| } |
| # Output the current comment |
| process_comment_text($current_comment); |
| output_open_api_file($current_comment->{COMMENT_NAME}); |
| output_api_header($current_comment); |
| output_api_name($current_comment); |
| output_api_comment($current_comment); |
| output_api_footer($current_comment); |
| output_close_api_file(); |
| } |
| |
| if ($opt_verbose > 2) |
| { |
| print "Setting current to ",$comment->{COMMENT_NAME},"\n"; |
| } |
| |
| my $comment_copy = |
| { |
| FILE => $comment->{FILE}, |
| COMMENT_NAME => $comment->{COMMENT_NAME}, |
| ALT_NAME => $comment->{ALT_NAME}, |
| DLL_NAME => $comment->{DLL_NAME}, |
| ORDINAL => $comment->{ORDINAL}, |
| RETURNS => $comment->{RETURNS}, |
| PROTOTYPE => [], |
| TEXT => [], |
| }; |
| |
| for (@{$comment->{TEXT}}) |
| { |
| push (@{$comment_copy->{TEXT}}, $_); |
| } |
| # Set this comment to be the current extra comment |
| @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy); |
| } |
| |
| # Write a standardised comment out in the appropriate format |
| sub output_comment($) |
| { |
| my $comment = shift; |
| |
| if ($opt_verbose > 0) |
| { |
| print "Processing ",$comment->{COMMENT_NAME},"\n"; |
| } |
| |
| if ($opt_verbose > 4) |
| { |
| print "--PROTO--\n"; |
| for (@{$comment->{PROTOTYPE}}) |
| { |
| print "'".$_."'\n"; |
| } |
| |
| print "--COMMENT--\n"; |
| for (@{$comment->{TEXT} }) |
| { |
| print $_."\n"; |
| } |
| } |
| |
| output_open_api_file($comment->{COMMENT_NAME}); |
| output_api_header($comment); |
| output_api_name($comment); |
| output_api_synopsis($comment); |
| output_api_comment($comment); |
| output_api_footer($comment); |
| output_close_api_file(); |
| } |
| |
| # Write out an index file for each .spec processed |
| sub process_index_files() |
| { |
| foreach my $spec_file (keys %spec_files) |
| { |
| my $spec_details = $spec_files{$spec_file}[0]; |
| if (defined ($spec_details->{DLL_NAME})) |
| { |
| if (@{$spec_details->{CURRENT_EXTRA}}) |
| { |
| # We have an unwritten extra comment, write it |
| my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0]; |
| process_extra_comment($current_comment); |
| @{$spec_details->{CURRENT_EXTRA}} = (); |
| } |
| output_spec($spec_details); |
| } |
| } |
| } |
| |
| # Write a spec files documentation out in the appropriate format |
| sub output_spec($) |
| { |
| my $spec_details = shift; |
| |
| if ($opt_verbose > 2) |
| { |
| print "Writing:",$spec_details->{DLL_NAME},"\n"; |
| } |
| |
| # Use the comment output functions for consistency |
| my $comment = |
| { |
| FILE => $spec_details->{DLL_NAME}, |
| COMMENT_NAME => $spec_details->{DLL_NAME}.".dll", |
| ALT_NAME => $spec_details->{DLL_NAME}, |
| DLL_NAME => "", |
| ORDINAL => "", |
| RETURNS => "", |
| PROTOTYPE => [], |
| TEXT => [], |
| }; |
| my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} + |
| $spec_details->{NUM_FUNCS}; |
| my $percent_implemented = 0; |
| if ($total_implemented) |
| { |
| $percent_implemented = $total_implemented / |
| ($total_implemented + $spec_details->{NUM_STUBS}) * 100; |
| } |
| $percent_implemented = int($percent_implemented); |
| my $percent_documented = 0; |
| if ($spec_details->{NUM_DOCS}) |
| { |
| # Treat forwards and data as documented funcs for statistics |
| $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100; |
| $percent_documented = int($percent_documented); |
| } |
| |
| # Make a list of the contributors to this DLL. Do this only for the source |
| # files that make up the DLL, because some directories specify multiple dlls. |
| my @contributors; |
| |
| for (@{$spec_details->{SOURCES}}) |
| { |
| my $source_details = $source_files{$_}[0]; |
| for (@{$source_details->{CONTRIBUTORS}}) |
| { |
| push (@contributors, $_); |
| } |
| } |
| |
| my %saw; |
| @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage |
| @contributors = sort @contributors; |
| |
| # Remove duplicates and blanks |
| for(my $i=0; $i<@contributors; $i++) |
| { |
| if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq "")) |
| { |
| $contributors[$i-1] = $contributors[$i]; |
| } |
| } |
| undef %saw; |
| @contributors = grep(!$saw{$_}++, @contributors); |
| |
| if ($opt_verbose > 3) |
| { |
| print "Contributors:\n"; |
| for (@contributors) |
| { |
| print "'".$_."'\n"; |
| } |
| } |
| my $contribstring = join (", ", @contributors); |
| |
| # Create the initial comment text |
| @{$comment->{TEXT}} = ( |
| "NAME", |
| $comment->{COMMENT_NAME} |
| ); |
| |
| # Add the description, if we have one |
| if (@{$spec_details->{DESCRIPTION}}) |
| { |
| push (@{$comment->{TEXT}}, "DESCRIPTION"); |
| for (@{$spec_details->{DESCRIPTION}}) |
| { |
| push (@{$comment->{TEXT}}, $_); |
| } |
| } |
| |
| # Add the statistics and contributors |
| push (@{$comment->{TEXT}}, |
| "STATISTICS", |
| "Forwards: ".$spec_details->{NUM_FORWARDS}, |
| "Variables: ".$spec_details->{NUM_VARS}, |
| "Stubs: ".$spec_details->{NUM_STUBS}, |
| "Functions: ".$spec_details->{NUM_FUNCS}, |
| "Exports-Total: ".$spec_details->{NUM_EXPORTS}, |
| "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)", |
| "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)", |
| "CONTRIBUTORS", |
| "The following people hold copyrights on the source files comprising this dll:", |
| "", |
| $contribstring, |
| "Note: This list may not be complete.", |
| "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.", |
| "", |
| ); |
| |
| if ($opt_output_format eq "h") |
| { |
| # Add the exports to the comment text |
| push (@{$comment->{TEXT}},"EXPORTS"); |
| my $exports = $spec_details->{EXPORTS}; |
| for (@$exports) |
| { |
| my $line = ""; |
| |
| # @$_ => ordinal, call convention, exported name, implementation name, flags; |
| if (@$_[1] eq "forward") |
| { |
| my $forward_dll = @$_[3]; |
| $forward_dll =~ s/\.(.*)//; |
| $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())"; |
| } |
| elsif (@$_[1] eq "extern") |
| { |
| $line = @$_[2]." (extern)"; |
| } |
| elsif (@$_[1] eq "stub") |
| { |
| $line = @$_[2]." (stub)"; |
| } |
| elsif (@$_[1] eq "fake") |
| { |
| # Don't add this function here, it gets listed with the extra documentation |
| if (!(@$_[4] & $FLAG_WPAIR)) |
| { |
| # This function should be indexed |
| push (@index_entries_list, @$_[3].",".@$_[3]); |
| } |
| } |
| elsif (@$_[1] eq "equate" || @$_[1] eq "variable") |
| { |
| $line = @$_[2]." (data)"; |
| } |
| else |
| { |
| # A function |
| if (@$_[4] & $FLAG_DOCUMENTED) |
| { |
| # Documented |
| $line = @$_[2]." (implemented as ".@$_[3]."())"; |
| if (@$_[2] ne @$_[3]) |
| { |
| $line = @$_[2]." (implemented as ".@$_[3]."())"; |
| } |
| else |
| { |
| $line = @$_[2]."()"; |
| } |
| if (!(@$_[4] & $FLAG_WPAIR)) |
| { |
| # This function should be indexed |
| push (@index_entries_list, @$_[2].",".@$_[3]); |
| } |
| } |
| else |
| { |
| $line = @$_[2]." (not documented)"; |
| } |
| } |
| if ($line ne "") |
| { |
| push (@{$comment->{TEXT}}, $line, ""); |
| } |
| } |
| |
| # Add links to the extra documentation |
| if (@{$spec_details->{EXTRA_COMMENTS}}) |
| { |
| push (@{$comment->{TEXT}}, "SEE ALSO"); |
| my %htmp; |
| @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}}); |
| for (@{$spec_details->{EXTRA_COMMENTS}}) |
| { |
| push (@{$comment->{TEXT}}, $_."()", ""); |
| } |
| } |
| } |
| # The dll entry should also be indexed |
| push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME}); |
| |
| # Write out the document |
| output_open_api_file($spec_details->{DLL_NAME}); |
| output_api_header($comment); |
| output_api_comment($comment); |
| output_api_footer($comment); |
| output_close_api_file(); |
| |
| # Add this dll to the database of dll names |
| my $output_file = $opt_output_directory."/dlls.db"; |
| |
| # Append the dllname to the output db of names |
| open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n"; |
| print DLLDB $spec_details->{DLL_NAME},"\n"; |
| close(DLLDB); |
| |
| if ($opt_output_format eq "s") |
| { |
| output_sgml_dll_file($spec_details); |
| return; |
| } |
| } |
| |
| # |
| # OUTPUT FUNCTIONS |
| # ---------------- |
| # Only these functions know anything about formatting for a specific |
| # output type. The functions above work only with plain text. |
| # This is to allow new types of output to be added easily. |
| |
| # Open the api file |
| sub output_open_api_file($) |
| { |
| my $output_name = shift; |
| $output_name = $opt_output_directory."/".$output_name; |
| |
| if ($opt_output_format eq "h") |
| { |
| $output_name = $output_name.".html"; |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| $output_name = $output_name.".sgml"; |
| } |
| else |
| { |
| $output_name = $output_name.".".$opt_manual_section; |
| } |
| open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n"; |
| } |
| |
| # Close the api file |
| sub output_close_api_file() |
| { |
| close (OUTPUT); |
| } |
| |
| # Output the api file header |
| sub output_api_header($) |
| { |
| my $comment = shift; |
| |
| if ($opt_output_format eq "h") |
| { |
| print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n"; |
| print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"; |
| print OUTPUT "<HTML>\n<HEAD>\n"; |
| print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n"; |
| print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n"; |
| print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n"; |
| print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n"; |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n", |
| "<sect1>\n", |
| "<title>$comment->{COMMENT_NAME}</title>\n"; |
| } |
| else |
| { |
| print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n". |
| ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"". |
| "Wine API\" \"Wine API\"\n"; |
| } |
| } |
| |
| sub output_api_footer($) |
| { |
| if ($opt_output_format eq "h") |
| { |
| print OUTPUT "<hr><p><i class=\"copy\">Copyright © ".$year." The Wine Project.". |
| " All trademarks are the property of their respective owners.". |
| " Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.". |
| " Generated $date.</i></p>\n</body>\n</html>\n"; |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| print OUTPUT "</sect1>\n"; |
| return; |
| } |
| else |
| { |
| } |
| } |
| |
| sub output_api_section_start($$) |
| { |
| my $comment = shift; |
| my $section_name = shift; |
| |
| if ($opt_output_format eq "h") |
| { |
| print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n"; |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n"; |
| } |
| else |
| { |
| print OUTPUT "\n\.SH ",$section_name,"\n"; |
| } |
| } |
| |
| sub output_api_section_end() |
| { |
| # Not currently required by any output formats |
| } |
| |
| sub output_api_name($) |
| { |
| my $comment = shift; |
| my $readable_name = $comment->{COMMENT_NAME}; |
| $readable_name =~ s/-/ /g; # make section names more readable |
| |
| output_api_section_start($comment,"NAME"); |
| |
| |
| my $dll_ordinal = ""; |
| if ($comment->{ORDINAL} ne "") |
| { |
| $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")"; |
| } |
| if ($opt_output_format eq "h") |
| { |
| print OUTPUT "<p><b class=\"func_name\">",$readable_name, |
| "</b> <i class=\"dll_ord\">", |
| ,$dll_ordinal,"</i></p>\n"; |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| print OUTPUT "<para>\n <command>",$readable_name,"</command> <emphasis>", |
| $dll_ordinal,"</emphasis>\n</para>\n"; |
| } |
| else |
| { |
| print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal; |
| } |
| |
| output_api_section_end(); |
| } |
| |
| sub output_api_synopsis($) |
| { |
| my $comment = shift; |
| my @fmt; |
| |
| output_api_section_start($comment,"SYNOPSIS"); |
| |
| if ($opt_output_format eq "h") |
| { |
| print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n"; |
| @fmt = ("", "\n", "<tt class=\"param\">", "</tt>"); |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n"; |
| @fmt = ("", "\n", "<emphasis>", "</emphasis>"); |
| } |
| else |
| { |
| print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n"; |
| @fmt = ("", "\n", "\\fI", "\\fR"); |
| } |
| |
| # Since our prototype is output in a pre-formatted block, line up the |
| # parameters and parameter comments in the same column. |
| |
| # First caluculate where the columns should start |
| my $biggest_length = 0; |
| for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++) |
| { |
| my $line = ${@{$comment->{PROTOTYPE}}}[$i]; |
| if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/) |
| { |
| my $length = length $1; |
| if ($length > $biggest_length) |
| { |
| $biggest_length = $length; |
| } |
| } |
| } |
| |
| # Now pad the string with blanks |
| for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++) |
| { |
| my $line = ${@{$comment->{PROTOTYPE}}}[$i]; |
| if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/) |
| { |
| my $pad_len = $biggest_length - length $1; |
| my $padding = " " x ($pad_len); |
| ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2; |
| } |
| } |
| |
| for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++) |
| { |
| # Format the parameter name |
| my $line = ${@{$comment->{PROTOTYPE}}}[$i]; |
| my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ","; |
| $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/; |
| print OUTPUT $line; |
| } |
| |
| if ($opt_output_format eq "h") |
| { |
| print OUTPUT " )\n\n</pre></p>\n"; |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| print OUTPUT " )\n</screen>\n"; |
| } |
| else |
| { |
| print OUTPUT " )\n"; |
| } |
| |
| output_api_section_end(); |
| } |
| |
| sub output_api_comment($) |
| { |
| my $comment = shift; |
| my $open_paragraph = 0; |
| my $open_raw = 0; |
| my $param_docs = 0; |
| my @fmt; |
| |
| if ($opt_output_format eq "h") |
| { |
| @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>", |
| "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>", |
| "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n", |
| "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n", |
| "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>"); |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>", |
| "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>", |
| "<screen>\n","</screen>\n", |
| "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n", |
| "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n", |
| "</entry>","</entry><entry>"); |
| } |
| else |
| { |
| @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR", |
| "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","",""); |
| } |
| |
| # Extract the parameter names |
| my @parameter_names; |
| for (@{$comment->{PROTOTYPE}}) |
| { |
| if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ ) |
| { |
| push (@parameter_names, $2); |
| } |
| } |
| |
| for (@{$comment->{TEXT}}) |
| { |
| if ($opt_output_format eq "h" || $opt_output_format eq "s") |
| { |
| # Map special characters |
| s/\&/\&/g; |
| s/\</\</g; |
| s/\>/\>/g; |
| s/\([Cc]\)/\©/g; |
| s/\(tm\)/®/; |
| } |
| |
| if ( s/^\|// ) |
| { |
| # Raw output |
| if ($open_raw == 0) |
| { |
| if ($open_paragraph == 1) |
| { |
| # Close the open paragraph |
| print OUTPUT $fmt[1]; |
| $open_paragraph = 0; |
| } |
| # Start raw output |
| print OUTPUT $fmt[12]; |
| $open_raw = 1; |
| } |
| if ($opt_output_format eq "") |
| { |
| print OUTPUT ".br\n"; # Prevent 'man' running these lines together |
| } |
| print OUTPUT $_,"\n"; |
| } |
| else |
| { |
| if ($opt_output_format eq "h") |
| { |
| # Link to the file in WineHQ cvs |
| s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g; |
| } |
| # Highlight strings |
| s/(\".+?\")/$fmt[2]$1$fmt[3]/g; |
| # Highlight literal chars |
| s/(\'.\')/$fmt[2]$1$fmt[3]/g; |
| s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g; |
| # Highlight numeric constants |
| s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g; |
| |
| # Leading cases ("xxxx:","-") start new paragraphs & are emphasised |
| # FIXME: Using bullet points for leading '-' would look nicer. |
| if ($open_paragraph == 1) |
| { |
| s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/; |
| s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/; |
| } |
| else |
| { |
| s/^(\-)/$fmt[4]$1$fmt[5]/; |
| s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/; |
| } |
| |
| if ($opt_output_format eq "h") |
| { |
| # Html uses links for API calls |
| while ( /([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/) |
| { |
| my $link = $1; |
| my $readable_link = $1; |
| $readable_link =~ s/-/ /g; |
| |
| s/([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/<a href\=\"$link\.html\">$readable_link<\/a>/; |
| } |
| # Index references |
| s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g; |
| s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g; |
| # And references to COM objects (hey, they'll get documented one day) |
| s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g; |
| # Convert any web addresses to real links |
| s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g; |
| } |
| else |
| { |
| if ($opt_output_format eq "") |
| { |
| # Give the man section for API calls |
| s/ ([A-Za-z_]+[A-Za-z_0-9-]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g; |
| } |
| else |
| { |
| # Highlight API calls |
| s/ ([A-Za-z_]+[A-Za-z_0-9-]+\(\))/ $fmt[6]$1$fmt[7]/g; |
| } |
| |
| # And references to COM objects |
| s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g; |
| } |
| |
| if ($open_raw == 1) |
| { |
| # Finish the raw output |
| print OUTPUT $fmt[13]; |
| $open_raw = 0; |
| } |
| |
| if ( /^[A-Z]+$/ || /^SEE ALSO$/ ) |
| { |
| # Start of a new section |
| if ($open_paragraph == 1) |
| { |
| if ($param_docs == 1) |
| { |
| print OUTPUT $fmt[17],$fmt[15]; |
| } |
| else |
| { |
| print OUTPUT $fmt[1]; |
| } |
| $open_paragraph = 0; |
| } |
| output_api_section_start($comment,$_); |
| if ( /^PARAMS$/ || /^MEMBERS$/ ) |
| { |
| print OUTPUT $fmt[14]; |
| $param_docs = 1; |
| } |
| else |
| { |
| #print OUTPUT $fmt[15]; |
| $param_docs = 0; |
| } |
| } |
| elsif ( /^$/ ) |
| { |
| # Empty line, indicating a new paragraph |
| if ($open_paragraph == 1) |
| { |
| if ($param_docs == 0) |
| { |
| print OUTPUT $fmt[1]; |
| $open_paragraph = 0; |
| } |
| } |
| } |
| else |
| { |
| if ($param_docs == 1) |
| { |
| if ($open_paragraph == 1) |
| { |
| # For parameter docs, put each parameter into a new paragraph/table row |
| print OUTPUT $fmt[17]; |
| $open_paragraph = 0; |
| } |
| s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out |
| } |
| else |
| { |
| # Within paragraph lines, prevent lines running together |
| $_ = $_." "; |
| } |
| |
| # Format parameter names where they appear in the comment |
| for my $parameter_name (@parameter_names) |
| { |
| s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\=\/])/$1$fmt[8]$2$fmt[9]$3/g; |
| } |
| # Structure dereferences include the dereferenced member |
| s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g; |
| s/(\-\>\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g; |
| |
| if ($open_paragraph == 0) |
| { |
| if ($param_docs == 1) |
| { |
| print OUTPUT $fmt[16]; |
| } |
| else |
| { |
| print OUTPUT $fmt[0]; |
| } |
| $open_paragraph = 1; |
| } |
| # Anything in all uppercase on its own gets emphasised |
| s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g; |
| |
| print OUTPUT $_; |
| } |
| } |
| } |
| if ($open_raw == 1) |
| { |
| print OUTPUT $fmt[13]; |
| } |
| if ($open_paragraph == 1) |
| { |
| print OUTPUT $fmt[1]; |
| } |
| } |
| |
| # Create the master index file |
| sub output_master_index_files() |
| { |
| if ($opt_output_format eq "") |
| { |
| return; # No master index for man pages |
| } |
| |
| if ($opt_output_format eq "h") |
| { |
| # Append the index entries to the output db of index entries |
| my $output_file = $opt_output_directory."/index.db"; |
| open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n"; |
| for (@index_entries_list) |
| { |
| $_ =~ s/A\,/\,/; |
| print INDEXDB $_."\n"; |
| } |
| close(INDEXDB); |
| } |
| |
| # Use the comment output functions for consistency |
| my $comment = |
| { |
| FILE => "", |
| COMMENT_NAME => "The Wine Api Guide", |
| ALT_NAME => "The Wine Api Guide", |
| DLL_NAME => "", |
| ORDINAL => "", |
| RETURNS => "", |
| PROTOTYPE => [], |
| TEXT => [], |
| }; |
| |
| if ($opt_output_format eq "s") |
| { |
| $comment->{COMMENT_NAME} = "Introduction"; |
| $comment->{ALT_NAME} = "Introduction", |
| } |
| elsif ($opt_output_format eq "h") |
| { |
| @{$comment->{TEXT}} = ( |
| "NAME", |
| $comment->{COMMENT_NAME}, |
| "INTRODUCTION", |
| ); |
| } |
| |
| # Create the initial comment text |
| push (@{$comment->{TEXT}}, |
| "This document describes the Api calls made available", |
| "by Wine. They are grouped by the dll that exports them.", |
| "", |
| "Please do not edit this document, since it is generated automatically", |
| "from the Wine source code tree. Details on updating this documentation", |
| "are given in the \"Wine Developers Guide\".", |
| "CONTRIBUTORS", |
| "Api documentation is generally written by the person who ", |
| "implements a given Api call. Authors of each dll are listed in the overview ", |
| "section for that dll. Additional contributors who have updated source files ", |
| "but have not entered their names in a copyright statement are noted by an ", |
| "entry in the file \"Changelog\" from the Wine source code distribution.", |
| "" |
| ); |
| |
| # Read in all dlls from the database of dll names |
| my $input_file = $opt_output_directory."/dlls.db"; |
| my @dlls = `cat $input_file|sort|uniq`; |
| |
| if ($opt_output_format eq "h") |
| { |
| # HTML gets a list of all the dlls and an index. For docbook the index creates this for us |
| push (@{$comment->{TEXT}}, |
| "INDEX", |
| "For an alphabetical listing of the functions available, please click the ", |
| "first letter of the functions name below:","", |
| "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ". |
| "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ". |
| "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "", |
| "DLLS", |
| "Each dll provided by Wine is documented individually. The following dlls are provided :", |
| "" |
| ); |
| # Add the dlls to the comment |
| for (@dlls) |
| { |
| $_ =~ s/(\..*)?\n/\(\)/; |
| push (@{$comment->{TEXT}}, $_, ""); |
| } |
| output_open_api_file("index"); |
| } |
| elsif ($opt_output_format eq "s") |
| { |
| # Just write this as the initial blurb, with a chapter heading |
| output_open_api_file("blurb"); |
| print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n" |
| } |
| |
| # Write out the document |
| output_api_header($comment); |
| output_api_comment($comment); |
| output_api_footer($comment); |
| if ($opt_output_format eq "s") |
| { |
| print OUTPUT "</chapter>\n" # finish the chapter |
| } |
| output_close_api_file(); |
| |
| if ($opt_output_format eq "s") |
| { |
| output_sgml_master_file(\@dlls); |
| return; |
| } |
| if ($opt_output_format eq "h") |
| { |
| output_html_index_files(); |
| output_html_stylesheet(); |
| return; |
| } |
| } |
| |
| # Write the master wine-api.sgml, linking it to each dll. |
| sub output_sgml_master_file($) |
| { |
| my $dlls = shift; |
| |
| output_open_api_file("wine-api"); |
| print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n"; |
| print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n"; |
| print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n"; |
| |
| # List the entities |
| for (@$dlls) |
| { |
| $_ =~ s/(\..*)?\n//; |
| print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n" |
| } |
| |
| print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n"; |
| print OUTPUT " &blurb;\n"; |
| |
| for (@$dlls) |
| { |
| print OUTPUT " &",$_,";\n" |
| } |
| print OUTPUT "\n\n</book>\n"; |
| |
| output_close_api_file(); |
| } |
| |
| # Produce the sgml for the dll chapter from the generated files |
| sub output_sgml_dll_file($) |
| { |
| my $spec_details = shift; |
| |
| # Make a list of all the documentation files to include |
| my $exports = $spec_details->{EXPORTS}; |
| my @source_files = (); |
| for (@$exports) |
| { |
| # @$_ => ordinal, call convention, exported name, implementation name, documented; |
| if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" && |
| @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1) |
| { |
| # A documented function |
| push (@source_files,@$_[3]); |
| } |
| } |
| |
| push (@source_files,@{$spec_details->{EXTRA_COMMENTS}}); |
| |
| @source_files = sort @source_files; |
| |
| # create a new chapter for this dll |
| my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp"; |
| open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n"; |
| print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n"; |
| output_close_api_file(); |
| |
| # Add the sorted documentation, cleaning up as we go |
| `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`; |
| for (@source_files) |
| { |
| `cat $opt_output_directory/$_.sgml >>$tmp_name`; |
| `rm -f $opt_output_directory/$_.sgml`; |
| } |
| |
| # close the chapter, and overwite the dll source |
| open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n"; |
| print OUTPUT "</chapter>\n"; |
| close OUTPUT; |
| `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`; |
| } |
| |
| # Write the html index files containing the function names |
| sub output_html_index_files() |
| { |
| if ($opt_output_format ne "h") |
| { |
| return; |
| } |
| |
| my @letters = ('_', 'A' .. 'Z'); |
| |
| # Read in all functions |
| my $input_file = $opt_output_directory."/index.db"; |
| my @funcs = `cat $input_file|sort|uniq`; |
| |
| for (@letters) |
| { |
| my $letter = $_; |
| my $comment = |
| { |
| FILE => "", |
| COMMENT_NAME => "", |
| ALT_NAME => "", |
| DLL_NAME => "", |
| ORDINAL => "", |
| RETURNS => "", |
| PROTOTYPE => [], |
| TEXT => [], |
| }; |
| |
| $comment->{COMMENT_NAME} = $letter." Functions"; |
| $comment->{ALT_NAME} = $letter." Functions"; |
| |
| push (@{$comment->{TEXT}}, |
| "NAME", |
| $comment->{COMMENT_NAME}, |
| "FUNCTIONS" |
| ); |
| |
| # Add the functions to the comment |
| for (@funcs) |
| { |
| my $first_char = substr ($_, 0, 1); |
| $first_char = uc $first_char; |
| |
| if ($first_char eq $letter) |
| { |
| my $name = $_; |
| my $file; |
| $name =~ s/(^.*?)\,(.*?)\n/$1/; |
| $file = $2; |
| push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}",""); |
| } |
| } |
| |
| # Write out the document |
| output_open_api_file($letter); |
| output_api_header($comment); |
| output_api_comment($comment); |
| output_api_footer($comment); |
| output_close_api_file(); |
| } |
| } |
| |
| # Output the stylesheet for HTML output |
| sub output_html_stylesheet() |
| { |
| if ($opt_output_format ne "h") |
| { |
| return; |
| } |
| |
| my $css; |
| ($css = <<HERE_TARGET) =~ s/^\s+//gm; |
| /* |
| * Default styles for Wine HTML Documentation. |
| * |
| * This style sheet should be altered to suit your needs/taste. |
| */ |
| BODY { /* Page body */ |
| background-color: white; |
| color: black; |
| font-family: Tahoma,sans-serif; |
| font-style: normal; |
| font-size: 10pt; |
| } |
| a:link { color: #4444ff; } /* Links */ |
| a:visited { color: #333377 } |
| a:active { color: #0000dd } |
| H2.section { /* Section Headers */ |
| font-family: sans-serif; |
| color: #777777; |
| background-color: #F0F0FE; |
| margin-left: 0.2in; |
| margin-right: 1.0in; |
| } |
| b.func_name { /* Function Name */ |
| font-size: 10pt; |
| font-style: bold; |
| } |
| i.dll_ord { /* Italicised DLL+ordinal */ |
| color: #888888; |
| font-family: sans-serif; |
| font-size: 8pt; |
| } |
| p { /* Paragraphs */ |
| margin-left: 0.5in; |
| margin-right: 0.5in; |
| } |
| table { /* tables */ |
| margin-left: 0.5in; |
| margin-right: 0.5in; |
| } |
| pre.proto /* API Function prototype */ |
| { |
| border-style: solid; |
| border-width: 1px; |
| border-color: #777777; |
| background-color: #F0F0BB; |
| color: black; |
| font-size: 10pt; |
| vertical-align: top; |
| margin-left: 0.5in; |
| margin-right: 1.0in; |
| } |
| pre.raw { /* Raw text output */ |
| margin-left: 0.6in; |
| margin-right: 1.1in; |
| background-color: #8080DC; |
| } |
| tt.param { /* Parameter name */ |
| font-style: italic; |
| color: blue; |
| } |
| tt.const { /* Constant */ |
| color: red; |
| } |
| i.in_out { /* In/Out */ |
| font-size: 8pt; |
| color: grey; |
| } |
| tt.coderef { /* Code in description text */ |
| color: darkgreen; |
| } |
| b.emp /* Emphasis */ { |
| font-style: bold; |
| color: darkblue; |
| } |
| i.footer { /* Footer */ |
| font-family: sans-serif; |
| font-size: 6pt; |
| color: darkgrey; |
| } |
| HERE_TARGET |
| |
| my $output_file = "$opt_output_directory/apidoc.css"; |
| open(CSS,">$output_file") || die "Couldn't create the file $output_file\n"; |
| print CSS $css; |
| close(CSS); |
| } |
| |
| |
| sub usage() |
| { |
| print "\nCreate API Documentation from Wine source code.\n\n", |
| "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n", |
| "Where: <spec> is a .spec file giving a DLL's exports.\n", |
| " <include> is an include directory used by the DLL.\n", |
| " <source> is a source file of the DLL.\n", |
| " The above can be given multiple times on the command line, as appropriate.\n", |
| "Options:\n", |
| " -Th : Output HTML instead of a man page\n", |
| " -Ts : Output SGML (Docbook source) instead of a man page\n", |
| " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n", |
| " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n", |
| " -e : Output \"FIXME\" documentation from empty comments.\n", |
| " -v : Verbosity. Can be given more than once for more detail.\n"; |
| } |
| |
| |
| # |
| # Main |
| # |
| |
| # Print usage if we're called with no args |
| if( @ARGV == 0) |
| { |
| usage(); |
| } |
| |
| # Process command line options |
| while(defined($_ = shift @ARGV)) |
| { |
| if( s/^-// ) |
| { |
| # An option. |
| for ($_) |
| { |
| /^o$/ && do { $opt_output_directory = shift @ARGV; last; }; |
| s/^S// && do { $opt_manual_section = $_; last; }; |
| /^Th$/ && do { $opt_output_format = "h"; last; }; |
| /^Ts$/ && do { $opt_output_format = "s"; last; }; |
| /^v$/ && do { $opt_verbose++; last; }; |
| /^e$/ && do { $opt_output_empty = 1; last; }; |
| /^L$/ && do { last; }; |
| /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; }; |
| s/^I// && do { if ($_ ne ".") { |
| my $include = $_."/*.h"; |
| $include =~ s/\/\//\//g; |
| my $have_headers = `ls $include >/dev/null 2>&1`; |
| if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); } |
| } |
| last; |
| }; |
| s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; } |
| else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; } |
| $opt_wine_root_dir =~ s/\n//; |
| $opt_wine_root_dir =~ s/\/\//\//g; |
| if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; }; |
| last; |
| }; |
| die "Unrecognised option $_\n"; |
| } |
| } |
| else |
| { |
| # A source file. |
| push (@opt_source_file_list, $_); |
| } |
| } |
| |
| # Remove duplicate include directories |
| my %htmp; |
| @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list); |
| |
| if ($opt_verbose > 3) |
| { |
| print "Output dir:'".$opt_output_directory."'\n"; |
| print "Section :'".$opt_manual_section."'\n"; |
| print "Format :'".$opt_output_format."'\n"; |
| print "Root :'".$opt_wine_root_dir."'\n"; |
| print "Spec files:'@opt_spec_file_list'\n"; |
| print "Includes :'@opt_header_file_list'\n"; |
| print "Sources :'@opt_source_file_list'\n"; |
| } |
| |
| if (@opt_spec_file_list == 0) |
| { |
| exit 0; # Don't bother processing non-dll files |
| } |
| |
| # Read in each .spec files exports and other details |
| while(my $spec_file = shift @opt_spec_file_list) |
| { |
| process_spec_file($spec_file); |
| } |
| |
| if ($opt_verbose > 3) |
| { |
| foreach my $spec_file ( keys %spec_files ) |
| { |
| print "in '$spec_file':\n"; |
| my $spec_details = $spec_files{$spec_file}[0]; |
| my $exports = $spec_details->{EXPORTS}; |
| for (@$exports) |
| { |
| print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n"; |
| } |
| } |
| } |
| |
| # Extract and output the comments from each source file |
| while(defined($_ = shift @opt_source_file_list)) |
| { |
| process_source_file($_); |
| } |
| |
| # Write the index files for each spec |
| process_index_files(); |
| |
| # Write the master index file |
| output_master_index_files(); |
| |
| exit 0; |