| #!/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; | |
| } |