mirror of
https://github.com/reactos/wine.git
synced 2024-11-25 04:39:45 +00:00
95c11f2b57
Allow '-' in comment names, convert it to space on display. Allow struct members to be documented (automatically, one day). Allow for many comments which start with "name (dll.ord) description".
2229 lines
62 KiB
Perl
Executable File
2229 lines
62 KiB
Perl
Executable File
#! /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;
|