#!/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 $current_dir = "."; if(length($wine_dir) != 1) { my $pwd; chomp($pwd = `pwd`); foreach my $n (1..((length($wine_dir) + 1) / 3)) { $pwd =~ s/\/([^\/]*)$//; $current_dir = "$1/$current_dir"; } $current_dir =~ s/\/.$//; } 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, $wine_dir, $current_dir, "$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"; } } if($options->global) { 'winapi'->read_all_spec_files($modules, $wine_dir, $current_dir, \&file_type, $win16api, $win32api); } else { my @spec_files = $modules->allowed_spec_files($wine_dir, $current_dir); 'winapi'->read_spec_files($modules, $wine_dir, $current_dir, \@spec_files, $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() { 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 = $modules->allowed_modules_in_file("$current_dir/$file"); my $file_module32 = $modules->allowed_modules_in_file("$current_dir/$file"); $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 $internal_name = shift; my $external_name = $internal_name; 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($internal_name) if $options->win16; $win32api->found_function($internal_name) if $options->win32; } if($file_type ne "application") { my $module16 = $win16api->function_module($internal_name); if(!defined($module16)) { if($internal_name =~ /^(.*?)_(.*?)$/) { my $module2 = lc($1); my $name2 = $2; if($win16api->is_module($module2)) { $module16 = $win16api->function_module($name2); if(defined($module16)) { if(uc($module16) !~ /\U$module2\E/) { $external_name = $name2; } else { $module16 = undef; } } } } } my $module32 = $win32api->function_module($internal_name); if(!defined($module32)) { if($internal_name =~ /^(.*?)_(.*?)$/) { my $module2 = lc($1); my $name2 = $2; if($win32api->is_module($module2)) { $module32 = $win32api->function_module($name2); if(defined($module32)) { if(uc($module32) =~ /\U$module2\E/) { $external_name = $name2; } else { $module32 = undef; } } } } } my $function = 'winapi_function'->new; $functions{$internal_name} = $function; $function->documentation($documentation); $function->linkage($linkage); $function->file($file); $function->return_type($return_type); $function->calling_convention($calling_convention); $function->external_name($external_name); $function->internal_name($internal_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("$internal_name(" . join(",", @arguments) . "): $msg\n"); } }; my $output16 = &$output_module($module16); my $output32 = &$output_module($module32); if($options->local && $options->misplaced && $linkage ne "extern" && $statements) { 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("is misplaced\n"); } } 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("is misplaced"); } } } if($options->local && $options->headers) { if(!$declared_functions{$internal_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, $external_name, $internal_name, [@arguments], $win16api; } if($options->win32 && $options->report_module($module32)) { winapi_local::check_function $options, $output32, $return_type, $calling_convention, $external_name, $internal_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{$internal_name}->function_called($called_name); if(!defined($functions{$called_name})) { $functions{$called_name} = 'winapi_function'->new; } $functions{$called_name}->function_called_by($internal_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) { my $name1; my $name2; if(defined($module16) && !defined($module32)) { my @uc_modules16 = split(/\s*\&\s*/, uc($module16)); push @uc_modules16, "WIN16"; $name1 = $internal_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 = $internal_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 = $internal_name; foreach my $uc_module (@uc_modules) { if($name1 =~ s/^$uc_module\_//) { last; } } $name2 = $name1; } if($documentation !~ /\b($internal_name|$name1|$name2)\b/) { $output->write("$file: $internal_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: $internal_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; }