1999-11-07 23:35:03 +00:00

367 lines
10 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 "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 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);
if($options->help) {
$options->show_help;
exit;
}
my $output = 'output'->new;
my $win16api = 'winapi'->new($output, "win16", "$winapi_check_dir/win16api.dat", "$winapi_check_dir/win16");
my $win32api = 'winapi'->new($output, "win32", "$winapi_check_dir/win32api.dat", "$winapi_check_dir/win32");
'winapi'->read_spec_files($wine_dir, $win16api, $win32api);
my $nativeapi = 'nativeapi'->new($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 "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");
foreach my $file2 (@files2) {
$includes{"include/$file2"}{used}++;
}
}
my $progress_output;
my $progress_current=0;
my $progress_max=scalar($options->files);
foreach my $file ($options->files) {
my %functions;
$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;
if($file_dir =~ /^(libtest|program|rc)/) {
$file_type = "application";
} elsif($file_dir =~ /^(debug|miscemu)/) {
$file_type = "emulator";
} elsif($file_dir =~ /^(tools)/) {
$file_type = "tool";
} else {
$file_type = "library";
}
my $found_function = sub {
my $documentation = 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($options->local) {
my $module16 = $win16api->function_module($name);
my $module32 = $win32api->function_module($name);
my $function = 'winapi_function'->new;
$functions{$name} = $function;
$function->documentation($documentation);
$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);
if($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->misplaced) {
my $module;
if($file =~ m'^dlls/(.*)/') {
$module = $1;
}
if($options->win16 && $options->report_module($module16)) {
if(!defined($module) || $module ne $module16) {
&$output16("function misplaced");
}
}
if($options->win32 && $options->report_module($module32)) {
if(!defined($module) || $module ne $module32) {
&$output32("function misplaced");
}
}
}
if($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} = 'function'->new;
}
$functions{$called_name}->function_called_by($name);
}
} else {
undef $_
}
}
}
if($options->documentation && (defined($module16) || defined($module32))) {
my $name2 = $name;
my $name3 = $name;
my $name4 = $name;
my $name5 = $name;
if(defined($module16) && !defined($module32)) {
$name2 =~ s/([AW])$/16$1/;
$name3 =~ s/16(([AW])?)$/$1/;
$name4 =~ s/^\U$module16\E\_//;
$name5 =~ s/^WIN16_//;
} elsif(!defined($module16) && defined($module32)) {
$name2 =~ s/([AW])$/32$1/;
$name3 =~ s/32(([AW])?)$/$1/;
$name4 =~ s/^\U$module32\E\_//;
$name5 =~ s/^WIN16_//;
}
if($name !~ /^SMapLS|SUnMapLS/ && $documentation !~ /($name|$name2|$name3|$name4|$name5)/) {
$output->write("$file: $name: \\\n");
$output->write("$documentation\n");
}
}
}
};
my $config = 0;
my $conditional = 0;
my $found_include = sub {
local $_ = shift;
if(/^\"(config\.h|native\.h)\"/) {
$config++;
}
};
my $found_conditional = sub {
local $_ = shift;
if(!$nativeapi->is_conditional($_)) {
if(/^HAVE_/ && !/^HAVE_(IPX|MESAGL|BUGGY_MESAGL|WINE_CONSTRUCTOR)$/)
{
$output->write("$file: $_ is not a 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") {
if($argument =~ /^<(.*?)>$/) {
my $header = $1;
if((-e "$wine_dir/include/$header" || -e "$file_dir/$header") && $file_type ne "application") {
$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");
}
} elsif($argument =~ /^"(.*?)"$/) {
my $header = $1;
if(-e "$file_dir/$header") {
$includes{"$file_dir/$header"}{used}++;
foreach my $name (keys(%{$includes{"$file_dir/$header"}{includes}})) {
$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) {
foreach my $name (sort(keys(%includes))) {
if(!$includes{$name}{used}) {
if($options->include) {
$output->write("$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;
}