
- Add new options --all and --none that enable/disables all check respectively. - Fixed and improved stub statistics. - Fixed bug that prevented checking of the first function in the .spec files. - Partial implementation of a more advanced misplaced function checking. - Minor fixes.
613 lines
16 KiB
Perl
Executable File
613 lines
16 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
|
|
# Copyright 1999 Patrik Stridvall
|
|
|
|
use strict;
|
|
|
|
my $wine_dir;
|
|
my $winapi_check_dir;
|
|
|
|
BEGIN {
|
|
|
|
if($0 =~ /^((.*?)\/?tools\/winapi_check)\/winapi_check$/)
|
|
{
|
|
$winapi_check_dir = $1;
|
|
if($2 ne "")
|
|
{
|
|
$wine_dir = $2;
|
|
} else {
|
|
$wine_dir = ".";
|
|
}
|
|
}
|
|
@INC = ($winapi_check_dir);
|
|
|
|
require "modules.pm";
|
|
require "nativeapi.pm";
|
|
require "output.pm";
|
|
require "preprocessor.pm";
|
|
require "winapi.pm";
|
|
require "winapi_function.pm";
|
|
require "winapi_local.pm";
|
|
require "winapi_global.pm";
|
|
require "winapi_options.pm";
|
|
require "winapi_parser.pm";
|
|
|
|
import modules;
|
|
import nativeapi;
|
|
import output;
|
|
import preprocessor;
|
|
import winapi;
|
|
import winapi_function;
|
|
import winapi_local;
|
|
import winapi_global;
|
|
import winapi_options;
|
|
import winapi_parser;
|
|
}
|
|
|
|
my $options = winapi_options->new(\@ARGV, $wine_dir);
|
|
if($options->help) {
|
|
$options->show_help;
|
|
exit;
|
|
}
|
|
|
|
my $output = 'output'->new;
|
|
|
|
my $modules = 'modules'->new($options, $output, "$winapi_check_dir/modules.dat");
|
|
|
|
my $win16api = 'winapi'->new($options, $output, "win16", "$winapi_check_dir/win16");
|
|
my $win32api = 'winapi'->new($options, $output, "win32", "$winapi_check_dir/win32");
|
|
|
|
sub file_type {
|
|
my $file = shift;
|
|
|
|
my $file_dir = $file;
|
|
if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
|
|
$file_dir = ".";
|
|
}
|
|
|
|
$file_dir =~ s/^$wine_dir\///;
|
|
|
|
if($file_dir =~ /^(libtest|program|rc|tools)/ ||
|
|
$file =~ /dbgmain\.c$/ ||
|
|
$file =~ /wineclipsrv\.c$/) # FIXME: Kludge
|
|
{
|
|
return "application";
|
|
} elsif($file_dir =~ /^(debug|miscemu)/) {
|
|
return "emulator";
|
|
} else {
|
|
return "library";
|
|
}
|
|
}
|
|
|
|
'winapi'->read_spec_files($wine_dir, \&file_type, $win16api, $win32api);
|
|
|
|
my $nativeapi = 'nativeapi'->new($options, $output, "$winapi_check_dir/nativeapi.dat", "$wine_dir/configure.in", "$wine_dir/include/config.h.in");
|
|
|
|
for my $name ($win32api->all_functions) {
|
|
my $module16 = $win16api->function_module($name);
|
|
my $module32 = $win32api->function_module($name);
|
|
|
|
if(defined($module16)) {
|
|
$win16api->found_shared_function($name);
|
|
$win32api->found_shared_function($name);
|
|
|
|
if($options->shared) {
|
|
$output->write("*.spec: $name: is shared between $module16 (Win16) and $module32 (Win32)\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
my %includes;
|
|
{
|
|
my @files = map {
|
|
s/^.\/(.*)$/$1/;
|
|
$_;
|
|
} split(/\n/, `find . -name \\*.h`);
|
|
|
|
foreach my $file (@files) {
|
|
my $file_dir = $file;
|
|
if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
|
|
$file_dir = ".";
|
|
}
|
|
|
|
$includes{$file} = { name => $file };
|
|
|
|
open(IN, "< $file");
|
|
while(<IN>) {
|
|
if(/^\s*\#\s*include\s*\"(.*?)\"/) {
|
|
my $header = $1;
|
|
if(-e "$file_dir/$header") {
|
|
$includes{$file}{includes}{"$file_dir/$header"}++;
|
|
} elsif(-e "$wine_dir/include/$header") {
|
|
$includes{$file}{includes}{"include/$header"}++;
|
|
} else {
|
|
$output->write("$file: #include \"$header\" is not a local include\n");
|
|
}
|
|
}
|
|
}
|
|
close(IN);
|
|
}
|
|
|
|
my @files2 = ("acconfig.h", "poppack.h", "pshpack1.h", "pshpack2.h", "pshpack4.h", "pshpack8.h",
|
|
"storage.h", "ver.h");
|
|
foreach my $file2 (@files2) {
|
|
$includes{"include/$file2"}{used}++;
|
|
}
|
|
}
|
|
|
|
my %declared_functions;
|
|
|
|
my $progress_output;
|
|
my $progress_current=0;
|
|
my $progress_max=scalar($options->c_files);
|
|
|
|
if($options->headers) {
|
|
$progress_max += scalar($options->h_files);
|
|
|
|
foreach my $file ($options->h_files) {
|
|
my %functions;
|
|
|
|
$progress_current++;
|
|
if($options->progress) {
|
|
$output->progress("$file: file $progress_current of $progress_max");
|
|
}
|
|
|
|
my $found_function = sub {
|
|
my $documentation = shift;
|
|
my $linkage = shift;
|
|
my $return_type = shift;
|
|
my $calling_convention = shift;
|
|
my $name = shift;
|
|
my $refarguments = shift;
|
|
my @arguments = @$refarguments;
|
|
my $statements = shift;
|
|
|
|
$declared_functions{$name}++;
|
|
};
|
|
|
|
my $found_preprocessor = sub {
|
|
my $directive = shift;
|
|
my $argument = shift;
|
|
};
|
|
|
|
winapi_parser::parse_c_file $options, $output, $file, $found_function, $found_preprocessor;
|
|
}
|
|
}
|
|
|
|
my %comment_width;
|
|
my %module_pseudo_stub_count16;
|
|
my %module_pseudo_stub_count32;
|
|
|
|
foreach my $file ($options->c_files) {
|
|
my %functions = ();
|
|
my $file_module16;
|
|
my $file_module32;
|
|
|
|
$progress_current++;
|
|
if($options->progress) {
|
|
$output->progress("$file: file $progress_current of $progress_max");
|
|
}
|
|
|
|
my $file_dir = $file;
|
|
if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
|
|
$file_dir = ".";
|
|
}
|
|
|
|
my $file_type = file_type($file);
|
|
|
|
my $found_function = sub {
|
|
my $documentation = shift;
|
|
my $linkage = shift;
|
|
my $return_type = shift;
|
|
my $calling_convention = shift;
|
|
my $name = shift;
|
|
my $refarguments = shift;
|
|
my @arguments = @$refarguments;
|
|
my $statements = shift;
|
|
|
|
if($options->global) {
|
|
$win16api->found_type($return_type) if $options->win16;
|
|
$win32api->found_type($return_type) if $options->win32;
|
|
for my $argument (@arguments) {
|
|
$win16api->found_type($argument) if $options->win16;
|
|
$win32api->found_type($argument) if $options->win32;
|
|
}
|
|
|
|
$win16api->found_function($name) if $options->win16;
|
|
$win32api->found_function($name) if $options->win32;
|
|
}
|
|
|
|
if($file_type ne "application") {
|
|
my $module16 = $win16api->function_module($name);
|
|
my $module32 = $win32api->function_module($name);
|
|
|
|
my $function = 'winapi_function'->new;
|
|
$functions{$name} = $function;
|
|
|
|
$function->documentation($documentation);
|
|
$function->linkage($linkage);
|
|
$function->file($file);
|
|
$function->return_type($return_type);
|
|
$function->calling_convention($calling_convention);
|
|
$function->name($name);
|
|
$function->arguments([@arguments]);
|
|
$function->statements($statements);
|
|
$function->module16($module16);
|
|
$function->module32($module32);
|
|
|
|
my $output_module = sub {
|
|
my $module = shift;
|
|
return sub {
|
|
my $msg = shift;
|
|
$output->write("$file: $module: $return_type ");
|
|
$output->write("$calling_convention ") if $calling_convention;
|
|
$output->write("$name(" . join(",", @arguments) . "): $msg\n");
|
|
}
|
|
};
|
|
my $output16 = &$output_module($module16);
|
|
my $output32 = &$output_module($module32);
|
|
|
|
# FIXME: Improve heuristics
|
|
if(!defined($file_module16)) {
|
|
$file_module16 = $module16;
|
|
}
|
|
|
|
# FIXME: Improve heuristics
|
|
if(!defined($file_module32)) {
|
|
$file_module32 = $module32;
|
|
}
|
|
|
|
if($options->local && $options->misplaced) {
|
|
if($options->win16 && $options->report_module($module16)) {
|
|
my $match = 0;
|
|
foreach my $module (split(/ & /, $module16)) {
|
|
foreach my $file_module (split(/ & /, $file_module16)) {
|
|
if($module eq $file_module) {
|
|
$match++;
|
|
}
|
|
}
|
|
}
|
|
if(!$match) {
|
|
&$output16("doesn't belong to file module ($file_module16)");
|
|
}
|
|
}
|
|
|
|
if($options->win32 && $options->report_module($module32)) {
|
|
my $match = 0;
|
|
foreach my $module (split(/ & /, $module32)) {
|
|
foreach my $file_module (split(/ & /, $file_module32)) {
|
|
if($module eq $file_module) {
|
|
$match++;
|
|
}
|
|
}
|
|
}
|
|
if(!$match) {
|
|
&$output32("doesn't belong to file module ($file_module32)");
|
|
}
|
|
}
|
|
}
|
|
|
|
if($options->local && $options->headers) {
|
|
if(!$declared_functions{$name}) {
|
|
if($options->win16 && $options->report_module($module16)) {
|
|
&$output16("no prototype");
|
|
}
|
|
if($options->win32 && $options->report_module($module32)) {
|
|
&$output32("no prototype");
|
|
}
|
|
}
|
|
}
|
|
|
|
if($options->local && $options->argument) {
|
|
if($options->win16 && $options->report_module($module16)) {
|
|
winapi_local::check_function $options, $output16,
|
|
$return_type, $calling_convention, $name, [@arguments], $win16api;
|
|
}
|
|
if($options->win32 && $options->report_module($module32)) {
|
|
winapi_local::check_function $options, $output32,
|
|
$return_type, $calling_convention, $name, [@arguments], $win32api;
|
|
}
|
|
}
|
|
|
|
if($options->local && $options->cross_call) {
|
|
local $_ = $statements;
|
|
my $called_function_names = {};
|
|
while(defined($_)) {
|
|
if(/(\w+)\((.*?)\)/) {
|
|
$_ = $';
|
|
my $called_name = $1;
|
|
if($called_name !~ /^if|for|while|switch|sizeof$/) {
|
|
$functions{$name}->function_called($called_name);
|
|
if(!defined($functions{$called_name})) {
|
|
$functions{$called_name} = 'winapi_function'->new;
|
|
}
|
|
$functions{$called_name}->function_called_by($name);
|
|
}
|
|
} else {
|
|
undef $_
|
|
}
|
|
}
|
|
}
|
|
|
|
if($options->stubs) {
|
|
if(defined($statements) && $statements =~ /FIXME[^;]*stub/) {
|
|
if($options->win16 && $options->report_module($module16)) {
|
|
foreach my $module (split(/ \& /, $module16)) {
|
|
$module_pseudo_stub_count16{$module}++;
|
|
}
|
|
}
|
|
if($options->win32 && $options->report_module($module32)) {
|
|
foreach my $module (split(/ \& /, $module32)) {
|
|
$module_pseudo_stub_count32{$module}++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if($options->local && $options->documentation &&
|
|
(defined($module16) || defined($module32)) &&
|
|
$linkage ne "extern" && $statements ne "")
|
|
{
|
|
my $name1;
|
|
my $name2;
|
|
|
|
if(defined($module16) && !defined($module32)) {
|
|
my @uc_modules16 = split(/\s*\&\s*/, uc($module16));
|
|
push @uc_modules16, "WIN16";
|
|
|
|
$name1 = $name;
|
|
foreach my $uc_module16 (@uc_modules16) {
|
|
if($name1 =~ s/^$uc_module16\_//) { last; }
|
|
}
|
|
|
|
# FIXME: This special case is becuase of a very ugly kludge that should be fixed IMHO
|
|
$name2 = $name1;
|
|
$name2 = s/^(.*?)16_fn(.*?)$/$116_$2/;
|
|
} elsif(!defined($module16) && defined($module32)) {
|
|
my @uc_modules32 = split(/\s*\&\s*/, uc($module32));
|
|
|
|
$name1 = $name;
|
|
foreach my $uc_module32 (@uc_modules32) {
|
|
if($name1 =~ s/^$uc_module32\_//) { last; }
|
|
}
|
|
|
|
$name2 = $name1;
|
|
$name2 =~ s/AW$//;
|
|
} else {
|
|
my @uc_modules = split(/\s*\&\s*/, uc($module16));
|
|
push @uc_modules, split(/\s*\&\s*/, uc($module32));
|
|
|
|
$name1 = $name;
|
|
foreach my $uc_module (@uc_modules) {
|
|
if($name1 =~ s/^$uc_module\_//) { last; }
|
|
}
|
|
|
|
$name2 = $name1;
|
|
}
|
|
|
|
if($documentation !~ /\b($name|$name1|$name2)\b/) {
|
|
$output->write("$file: $name: \\\n");
|
|
$output->write("$documentation\n");
|
|
}
|
|
|
|
if($options->documentation_width) {
|
|
if($documentation =~ /(\/\**)/) {
|
|
my $width = length($1);
|
|
|
|
$comment_width{$width}++;
|
|
if($width <= 65 || $width >= 81) {
|
|
$output->write("$file: $name: comment is $width columns wide\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
my $config = 0;
|
|
my $conditional = 0;
|
|
my $found_include = sub {
|
|
local $_ = shift;
|
|
if(/^\"config\.h\"/) {
|
|
$config++;
|
|
}
|
|
};
|
|
my $found_conditional = sub {
|
|
local $_ = shift;
|
|
|
|
if($options->config) {
|
|
if($file_type ne "application") {
|
|
if(!$nativeapi->is_conditional($_)) {
|
|
if(/^HAVE_/ && !/^HAVE_(IPX|MESAGL|BUGGY_MESAGL|WINE_CONSTRUCTOR)$/)
|
|
{
|
|
$output->write("$file: $_ is not declared as a conditional\n");
|
|
}
|
|
} else {
|
|
$conditional++;
|
|
if(!$config) {
|
|
$output->write("$file: conditional $_ used but config.h is not included\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
|
|
my $found_preprocessor = sub {
|
|
my $directive = shift;
|
|
my $argument = shift;
|
|
|
|
$preprocessor->directive($directive, $argument);
|
|
|
|
if($options->config) {
|
|
if($directive eq "include") {
|
|
my $header;
|
|
my $check_protection;
|
|
my $check_local;
|
|
if($argument =~ /^<(.*?)>$/) {
|
|
$header = $1;
|
|
if($file_type ne "application") {
|
|
$check_protection = 1;
|
|
} else {
|
|
$check_protection = 0;
|
|
}
|
|
$check_local = 0;
|
|
} elsif($argument =~ /^"(.*?)"$/) {
|
|
$header = $1;
|
|
$check_protection = 0;
|
|
$check_local = 1;
|
|
}
|
|
|
|
if($check_protection) {
|
|
if((-e "$wine_dir/include/$header" || -e "$file_dir/$header")) {
|
|
if($header !~ /^ctype.h$/) {
|
|
$output->write("$file: #include \<$header\> is a local include\n");
|
|
}
|
|
}
|
|
|
|
my $macro = uc($header);
|
|
$macro =~ y/\.\//__/;
|
|
$macro = "HAVE_" . $macro;
|
|
|
|
if($nativeapi->is_conditional_header($header)) {
|
|
if(!$preprocessor->is_def($macro)) {
|
|
if($macro =~ /^HAVE_X11/) {
|
|
if(!$preprocessor->is_undef("X_DISPLAY_MISSING")) {
|
|
$output->write("$file: #$directive $argument: is a conditional include, " .
|
|
"but is not protected\n");
|
|
}
|
|
} elsif($macro =~ /^HAVE_(.*?)_H$/) {
|
|
if($header ne "alloca.h" && !$preprocessor->is_def("STATFS_DEFINED_BY_$1")) {
|
|
$output->write("$file: #$directive $argument: is a conditional include, " .
|
|
"but is not protected\n");
|
|
}
|
|
}
|
|
}
|
|
} elsif($preprocessor->is_def($macro)) {
|
|
$output->write("$file: #$directive $argument: is protected, " .
|
|
"but is not a conditional include\n");
|
|
}
|
|
}
|
|
|
|
if($check_local) {
|
|
if(-e "$file_dir/$header") {
|
|
$includes{"$file_dir/$header"}{used}++;
|
|
foreach my $name (keys(%{$includes{"$file_dir/$header"}{includes}})) {
|
|
$includes{$name}{used}++;
|
|
}
|
|
} elsif(-e "$file_dir/../$header") { # FIXME: Kludge
|
|
$includes{"$file_dir/../$header"}{used}++; # FIXME: This is not correct
|
|
foreach my $name (keys(%{$includes{"$file_dir/../$header"}{includes}})) { # FIXME: This is not correct
|
|
$includes{$name}{used}++;
|
|
}
|
|
} elsif(-e "$wine_dir/include/$header") {
|
|
$includes{"include/$header"}{used}++;
|
|
foreach my $name (keys(%{$includes{"include/$header"}{includes}})) {
|
|
$includes{$name}{used}++;
|
|
}
|
|
} else {
|
|
$output->write("$file: #include \"$header\" is not a local include\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
winapi_parser::parse_c_file $options, $output, $file, $found_function, $found_preprocessor;
|
|
|
|
if($options->config_unnessary) {
|
|
if($config && $conditional == 0) {
|
|
$output->write("$file: includes config.h but do not use any conditionals\n");
|
|
}
|
|
}
|
|
|
|
winapi_local::check_file $options, $output, $file, \%functions;
|
|
}
|
|
|
|
$output->hide_progress;
|
|
|
|
if($options->global) {
|
|
if($options->documentation_width) {
|
|
foreach my $width (sort(keys(%comment_width))) {
|
|
my $count = $comment_width{$width};
|
|
$output->write("*.c: $count functions have comments of width $width\n");
|
|
}
|
|
}
|
|
|
|
if($options->stubs) {
|
|
if($options->win16) {
|
|
my %module_stub_count16;
|
|
my %module_total_count16;
|
|
|
|
foreach my $name ($win16api->all_functions,$win16api->all_functions_stub) {
|
|
foreach my $module (split(/ \& /, $win16api->function_module($name))) {
|
|
if($win16api->function_stub($name)) {
|
|
$module_stub_count16{$module}++;
|
|
}
|
|
$module_total_count16{$module}++;
|
|
}
|
|
}
|
|
|
|
foreach my $module ($win16api->all_modules) {
|
|
if($options->report_module($module)) {
|
|
my $real_stubs = $module_stub_count16{$module};
|
|
my $pseudo_stubs = $module_pseudo_stub_count16{$module};
|
|
|
|
if(!defined($real_stubs)) { $real_stubs = 0; }
|
|
if(!defined($pseudo_stubs)) { $pseudo_stubs = 0; }
|
|
|
|
my $stubs = $real_stubs + $pseudo_stubs;
|
|
my $total = $module_total_count16{$module};
|
|
|
|
if(!defined($total)) { $total = 0;}
|
|
|
|
$output->write("*.c: $module: ");
|
|
$output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo)\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if($options->win32) {
|
|
my %module_stub_count32;
|
|
my %module_total_count32;
|
|
|
|
foreach my $name ($win32api->all_functions,$win32api->all_functions_stub) {
|
|
foreach my $module (split(/ \& /, $win32api->function_module($name))) {
|
|
if($win32api->function_stub($name)) {
|
|
$module_stub_count32{$module}++;
|
|
}
|
|
$module_total_count32{$module}++;
|
|
}
|
|
}
|
|
|
|
foreach my $module ($win32api->all_modules) {
|
|
if($options->report_module($module)) {
|
|
my $real_stubs = $module_stub_count32{$module};
|
|
my $pseudo_stubs = $module_pseudo_stub_count32{$module};
|
|
|
|
if(!defined($real_stubs)) { $real_stubs = 0; }
|
|
if(!defined($pseudo_stubs)) { $pseudo_stubs = 0; }
|
|
|
|
my $stubs = $real_stubs + $pseudo_stubs;
|
|
my $total = $module_total_count32{$module};
|
|
|
|
if(!defined($total)) { $total = 0;}
|
|
|
|
$output->write("*.c: $module: ");
|
|
$output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo)\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach my $name (sort(keys(%includes))) {
|
|
if(!$includes{$name}{used}) {
|
|
if($options->include) {
|
|
$output->write("*.c: $name: include file is never used\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
winapi_global::check $options, $output, $win16api, $nativeapi if $options->win16;
|
|
winapi_global::check $options, $output, $win32api, $nativeapi if $options->win32;
|
|
}
|