API files update.
[wine] / tools / winapi_check / winapi_check
index 86b5fd6..4be3150 100755 (executable)
 #!/usr/bin/perl -w
 
-# Copyright 1999 Patrik Stridvall
+# Copyright 1999-2002 Patrik Stridvall
+#
+# 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
+#
+
+# Note that winapi_check are using heuristics quite heavily.
+# So always remember that:
+#
+# "Heuristics are bug ridden by definition.
+#  If they didn't have bugs, then they'd be algorithms."
+#
+# In other words, reported bugs are only potential bugs not
+# real bugs, so they are called issues rather than bugs.
+#
 
 use strict;
 
-my $wine_dir;
-my $winapi_check_dir;
-
 BEGIN {
+    $0 =~ m%^(.*?/?tools)/winapi_check/winapi_check$%;
+    require "$1/winapi/setup.pm";
+}
 
-    if($0 =~ /^((.*?)\/?tools\/winapi_check)\/winapi_check$/)
-    {
-       $winapi_check_dir = $1;
-       if($2 ne "")
-       {
-           $wine_dir = $2;
-       } else {
-           $wine_dir = ".";
-       }
+use config qw(
+    &files_filter &files_skip
+    &get_h_files
+    $current_dir $wine_dir
+);
+use output qw($output);
+use winapi_check_options qw($options);
+
+BEGIN {
+    if($options->progress) {
+       $output->enable_progress;
+    } else {
+       $output->disable_progress;
     }
-    @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, $wine_dir);
-if($options->help) {
-    $options->show_help;
-    exit;
-}
+use modules qw($modules);
+use nativeapi qw($nativeapi);
+use winapi qw($win16api $win32api @winapis);
 
-my $output = 'output'->new;
+use preprocessor;
+use type;
+use util qw(&is_subset);
+use winapi_documentation;
+use winapi_function;
+use winapi_local;
+use winapi_global;
+use winapi_parser;
 
-my $win16api = 'winapi'->new($options, $output, "win16", "$winapi_check_dir/win16");
-my $win32api = 'winapi'->new($options, $output, "win32", "$winapi_check_dir/win32");
-'winapi'->read_spec_files($wine_dir, $win16api, $win32api);
+my %include2info;
 
-my $nativeapi = 'nativeapi'->new($output, "$winapi_check_dir/nativeapi.dat", "$wine_dir/configure.in", "$wine_dir/include/config.h.in");
+if ($options->global) {
+    my @files = get_h_files("winelib");
 
-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 $progress_current = 0;
+    my $progress_max = scalar(@files);
 
-my %includes;
-{   
-    my @files = map {
-       s/^.\/(.*)$/$1/;
-       $_; 
-    } split(/\n/, `find . -name \\*.h`);
-    
     foreach my $file (@files) {
+       $progress_current++;
+       $output->lazy_progress("$file: file $progress_current of $progress_max");
+
        my $file_dir = $file;
-       if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
+       if(!($file_dir =~ s%(.*?)/[^/]+$%$1%)) {
            $file_dir = ".";
        }
-   
-       $includes{$file} = { name => $file };
 
-       open(IN, "< $file");
+       $include2info{$file} = { name => $file };
+
+       open(IN, "< $wine_dir/$file");
        while(<IN>) {
            if(/^\s*\#\s*include\s*\"(.*?)\"/) {
                my $header = $1;
-               if(-e "$file_dir/$header") {
-                   $includes{$file}{includes}{"$file_dir/$header"}++;
+               if(-e "$wine_dir/$file_dir/$header") {
+                   $include2info{$file}{includes}{"$file_dir/$header"}++;
+               } elsif(-e "$wine_dir/$file_dir/../$header") {
+                   if($file_dir =~ m%^(.*?)/[^/]+$%) {
+                       $include2info{$file}{includes}{"$1/$header"}++;
+                   } else {
+                       $include2info{$file}{includes}{"$header"}++;
+                   }
                } elsif(-e "$wine_dir/include/$header") {
-                   $includes{$file}{includes}{"include/$header"}++;
-               } else {                   
-                   $output->write("$file: #include \"$header\" is not a local include ($file_dir)\n");
+                   $include2info{$file}{includes}{"include/$header"}++;
+               } elsif ($header ne "config.h") {
+                   $output->write("$file: #include \"$header\" is not a local include\n");
                }
            }
        }
@@ -104,287 +107,397 @@ my %includes;
     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}++;
-    }    
+       $include2info{"include/$file2"}{used}++;
+    }
 }
 
-my %declared_functions;
+my @c_files = $options->c_files;
+@c_files = files_skip(@c_files);
+@c_files = files_filter("winelib", @c_files);
+
+my @h_files = $options->h_files;
+@h_files = files_skip(@h_files);
+@h_files = files_filter("winelib", @h_files);
+
+my $all_modules = 0;
+my %complete_module;
+if($options->global) {
+    my @complete_modules = $modules->complete_modules(\@c_files);
+
+    foreach my $module (@complete_modules) {
+       $complete_module{$module}++;
+    }
+
+    my $all_modules = 1;
+    foreach my $module ($modules->all_modules) {
+       if(!$complete_module{$module}) {
+           $all_modules = 0;
+           if($wine_dir eq ".") {
+               $output->write("*.c: module $module is not complete\n");
+           }
+       }
+    }
+}
+
+if(1) {
+    foreach my $winapi (@winapis) {
+       foreach my $broken_forward ($winapi->all_broken_forwards) {
+           (my $module, my $external_name, my $forward_module, my $forward_external_name) = @$broken_forward;
+           if($complete_module{$forward_module}) {
+               $output->write("$module.spec: forward is broken: $external_name => $forward_module.$forward_external_name\n");
+           }
+       }
+    }
+}
+
+my $progress_current = 0;
+my $progress_max = scalar(@c_files);
 
-my $progress_output;
-my $progress_current=0;
-my $progress_max=scalar($options->c_files);
+my %declared_functions;
 
 if($options->headers) {
-    $progress_max += scalar($options->h_files);
+    $progress_max += scalar(@h_files);
 
-    foreach my $file ($options->h_files) {
+    foreach my $file (@h_files) {
        my %functions;
-       
+
        $progress_current++;
-       if($options->progress) {
-           $output->progress("$file: file $progress_current of $progress_max");
-       }
-       
+       $output->progress("$file: file $progress_current of $progress_max");
+
+       my $found_c_comment = sub {
+           my $begin_line = shift;
+           my $end_line = shift;
+           my $comment = shift;
+
+           if(0) {
+               if($begin_line == $end_line) {
+                   $output->write("$file:$begin_line: $comment\n");
+               } else {
+                   $output->write("$file:$begin_line-$end_line: \\\n$comment\n");
+               }
+           }
+       };
+
+       my $found_cplusplus_comment = sub {
+           my $line = shift;
+           my $comment = shift;
+
+           if($options->comments_cplusplus) {
+               $output->write("$file:$line: C++ comments not allowed: $comment\n");
+           }
+       };
+
+       my $create_function = sub {
+           return 'winapi_function'->new;
+       };
+
        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 $function = shift;
+
+           my $internal_name = $function->internal_name;
+
+           $output->progress("$file (file $progress_current of $progress_max): $internal_name");
+           $output->prefix_callback(sub { return $function->prefix; });
+
+           my $function_line = $function->function_line;
+           my $linkage = $function->linkage;
+           my $external_name = $function->external_name;
+           my $statements = $function->statements;
+
+           if($options->headers_misplaced &&
+              !($function->is_win16 && $function->is_win32) &&
+              (($function->is_win16 && $file =~ /^include\/[^\/]*$/) ||
+               ($function->is_win32 && $file =~ /^include\/wine\/[^\/]*$/)))
+           {
+               $output->write("declaration misplaced\n");
+           }
+
+           if(defined($external_name) && !defined($statements) &&
+              ($linkage eq "" || $linkage eq "extern"))
+           {
+               my $previous_function = $declared_functions{$internal_name};
+               if(!defined($previous_function)) {
+                   $declared_functions{$internal_name} = $function;
+               } elsif($options->headers_duplicated) {
+                   my $file = $previous_function->file;
+                   my $function_line = $previous_function->function_line;
+                   if($file =~ /\.h$/) {
+                       $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
+                   }
+               }
+           }
+           $output->prefix("");
+       };
+
+       my $create_type = sub {
+           return 'type'->new;
        };
-       
+
+       my $found_type = sub {
+           my $type = shift;
+       };
+
        my $found_preprocessor = sub {
            my $directive = shift;
            my $argument = shift;
        };
-       
-        winapi_parser::parse_c_file $options, $output, $file, $found_function, $found_preprocessor;
+
+       &winapi_parser::parse_c_file($file, {
+           c_comment_found => $found_c_comment,
+           cplusplus_comment_found => $found_cplusplus_comment,
+           function_create => $create_function,
+           function_found => $found_function,
+           type_create => $create_type,
+           type_found => $found_type,
+           preprocessor_found => $found_preprocessor
+       });
     }
 }
 
-my %comment_width;
-my %module_pseudo_stub_count16;
-my %module_pseudo_stub_count32;
+my %module2functions = ();
 
-foreach my $file ($options->c_files) {
+foreach my $file (@c_files) {
     my %functions = ();
+    my %includes = ();
+
+    $includes{$file}++;
+
+    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");
-    }
+    $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|tools)/ || 
-       $file =~ /dbgmain\.c$/ ||
-       $file =~ /wineclipsrv\.c$/) # FIXME: Kludge
-    {
-       $file_type = "application";
-    } elsif($file_dir =~ /^(debug|miscemu)/) {
-       $file_type = "emulator";
-    } else {
-       $file_type = "library";
-    }
-    
-    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;
+    my $found_c_comment = sub {
+       my $begin_line = shift;
+       my $end_line = shift;
+       my $comment = shift;
+
+       if(0) {
+           if($begin_line == $end_line) {
+               $output->write("$file:$begin_line: $comment\n");
+           } else {
+               $output->write("$file:$begin_line-$end_line: \\\n$comment\n");
            }
-           
-           $win16api->found_function($name) if $options->win16;
-           $win32api->found_function($name) if $options->win32;
        }
-       
-       if($options->local && $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 $found_cplusplus_comment = sub {
+       my $line = shift;
+       my $comment = shift;
+
+       if($options->comments_cplusplus) {
+           $output->write("$file:$line: C++ comments not allowed: $comment\n");
+       }
+    };
+
+    my $create_function = sub {
+       return 'winapi_function'->new;
+    };
+
+    my $found_function = sub {
+       my $function = shift;
+
+       my $internal_name = $function->internal_name;
+       $functions{$internal_name} = $function;
+
+       $output->progress("$file (file $progress_current of $progress_max): $internal_name");
+       $output->prefix_callback(sub { return $function->prefix; });
+
+       my $declared_function = $declared_functions{$internal_name};
+
+       my $documentation_line = $function->documentation_line;
+       my $documentation = $function->documentation;
+       my $linkage = $function->linkage;
+       my $return_type = $function->return_type;
+       my $calling_convention = $function->calling_convention;
+       my $statements = $function->statements;
+
+       my $module16 = $function->module16;
+       my $module32 = $function->module32;
+
+       my $external_name = $function->external_name;
+       my $external_name16 = $function->external_name16;
+       my $external_name32 = $function->external_name32;
+
+       if(defined($external_name) && !defined($statements) &&
+          ($linkage eq "" || $linkage eq "extern"))
+       {
+           my $previous_function = $declared_functions{$internal_name};
+           if(!defined($previous_function)) {
+               $declared_functions{$internal_name} = $function;
+           } else {
+               my $file = $previous_function->file;
+               my $function_line = $previous_function->function_line;
+
+               my $header = $file;
+               $header =~ s%^(include|$file_dir)/%%;
+               if($header !~ m%^msvcrt/% || $file_dir =~ m%^dlls/msvcrt%) {
+                   $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
                }
-           };
-           my $output16 = &$output_module($module16);
-           my $output32 = &$output_module($module32);
-
-           if($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->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->global) {
+           foreach my $module ($function->modules) {
+               $module2functions{$module}{$internal_name} = $function;
            }
-           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");
-                   }
+       foreach my $module ($function->modules) {
+           $modules->found_module_in_dir($module, $file_dir);
+       }
+
+       if($options->shared) {
+           if($win16api->is_shared_internal_function($internal_name) ||
+              $win32api->is_shared_internal_function($internal_name))
+           {
+               $output->write("is shared between Win16 and Win32\n");
+           }
+       }
+
+       if($options->headers && $options->headers_needed &&
+          defined($declared_function) && defined($external_name) &&
+          defined($statements))
+       {
+           my $needed_include = $declared_function->file;
+
+           if(!defined($includes{$needed_include})) {
+               my $header = $needed_include;
+               $header =~ s%^(include|$file_dir)/%%;
+               if($header !~ m%^msvcrt/% || $file_dir =~ m%^dlls/msvcrt%) {
+                   $output->write("prototype not included: #include \"$header\" is needed\n");
                }
-               
-               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;
+       }
+
+       if($options->local && $options->argument && defined($statements)) {
+           &winapi_local::check_function($function);
+       }
+
+       if($options->local && $options->statements && defined($statements)) {
+           &winapi_local::check_statements(\%functions, $function);
+       }
+
+       if($options->local && $options->documentation &&
+          (defined($module16) || defined($module32)) &&
+          $linkage eq "" && defined($statements))
+       {
+           &winapi_documentation::check_documentation($function);
+       }
+
+       if(1) {
+           # FIXME: Not correct
+           if(defined($external_name16)) {
+               $external_name16 = (split(/\s*&\s*/, $external_name16))[0];
+           }
+
+           # FIXME: Not correct
+           if(defined($external_name32)) {
+               $external_name32 = (split(/\s*&\s*/, $external_name32))[0];
+           }
+
+           if($options->local && $options->misplaced &&
+              $linkage ne "extern" && defined($statements))
+           {
+               if($options->win16 && $options->report_module($module16))
+               {
+                   if($file ne "library/port.c" &&
+                      !$nativeapi->is_function($internal_name) &&
+                      !is_subset($module16, $file_module16))
+                   {
+                       foreach my $module16 (split(/\s*&\s*/, $module16)) {
+                           if(!$win16api->is_function_stub($module16, $internal_name)) {
+                               $output->write("is misplaced ($module16)\n");
                            }
-                           $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->win32 && $options->report_module($module32))
+               {
+                   if($file ne "library/port.c" &&
+                      !$nativeapi->is_function($internal_name) &&
+                      !is_subset($module32, $file_module32))
+                   {
+                       foreach my $module32 (split(/\s*&\s*/, $module32)) {
+                           if(!$win32api->is_function_stub($module32, $internal_name)) {
+                               $output->write("is misplaced ($module32)\n");
+                           }
                        }
                    }
                }
            }
-           
-           if($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; }
+           if($options->local && $options->headers && $options->prototype) {
+               if($options->win16 && $options->report_module($module16)) {
+                   if(!$nativeapi->is_function($internal_name) &&
+                      !defined($declared_functions{$internal_name}))
+                   {
+                       $output->write("no prototype\n");
                    }
-
-                   $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");
+               if($options->win32 && $options->report_module($module32)) {
+                   if(!defined($external_name32) || (!$nativeapi->is_function($external_name32) &&                                                       !defined($declared_functions{$external_name32})))
+                   {
+                       if(!defined($external_name32) || ($external_name32 !~ /^Dll(?:
+                          Install|CanUnloadNow|GetClassObject|GetVersion|
+                          RegisterServer|RegisterServerEx|UnregisterServer)|DriverProc$/x &&
+                          $internal_name !~ /^COMCTL32_Str/ &&
+                          $internal_name !~ /^(?:\Q$module32\E|wine)_(?:\Q$external_name32\E|\d+)$/))
+                       {
+                           $output->write("no prototype\n");
                        }
                    }
                }
            }
        }
+
+       $output->prefix("");
     };
 
     my $config = 0;
     my $conditional = 0;
     my $found_include = sub {
        local $_ = shift;
-       if(/^\"config\.h\"/) {
+       if(/^\"(?:config\.h|wine\/port\.h)\"/) {
            $config++;
        }
     };
     my $found_conditional = sub {
-       if($file_type ne "application") {
-          local $_ = shift;
-          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");
-              }
-          }
-       }
+       local $_ = shift;
+
+       $nativeapi->found_conditional($_);
+
+       if($options->config) {
+           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 $create_type = sub {
+       return 'type'->new;
+    };
+
+    my $found_type = sub {
+       my $type = shift;
+    };
+
     my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
     my $found_preprocessor = sub {
        my $directive = shift;
@@ -398,22 +511,60 @@ foreach my $file ($options->c_files) {
                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;
+                   $header = $1;
+                   $check_protection = 1;
+                   $check_local = 0;
+               } elsif($argument =~ /^\"(.*?)\"$/) {
+                   $header = $1;
+                   $check_protection = 0;
+                   $check_local = 1;
+               } else {
+                   $output->write("$file: #$directive $argument: is unparsable\n");
+
+                   $header = undef;
+                   $check_protection = 0;
+                   $check_local = 0;
                }
 
-               if($check_protection) {
-                   if((-e "$wine_dir/include/$header" || -e "$file_dir/$header")) {
-                       if($header !~ /^ctype.h$/) {
+               if(defined($header)) {
+                   my $include;
+                   if(-e "$wine_dir/include/$header") {
+                       $include = "include/$header";
+                   } elsif(-e "$file_dir/$header") {
+                       $include = "$file_dir/$header";
+                   } elsif(-e "$file_dir/../$header") {
+                       if($file_dir =~ m%^(.*?)/[^/]+$%) {
+                           $include = "$1/$header";
+                       } else {
+                           $include = "$header";
+                       }
+                   } elsif($header =~ /^(?:kernel_private\.h)$/) { # FIXME: Kludge
+                       $include = "dlls/kernel/$header";
+                   } elsif($header =~ /^(?:gdi_private\.h)$/) { # FIXME: Kludge
+                       $include = "dlls/gdi/$header";
+                   } elsif($header =~ /^(?:ntdll_misc\.h)$/) { # FIXME: Kludge
+                       $include = "dlls/ntdll/$header";
+                   } elsif($header =~ /^(?:controls\.h|message\.h)$/) { # FIXME: Kludge
+                       $include = "dlls/user/$header";
+                   } elsif($header =~ /^(?:ts_xlib\.h|winproc\.h|x11drv\.h|x11font\.h)$/) { # FIXME: Kludge
+                       $include = "dlls/x11drv/$header";
+                   } elsif($check_local && $header ne "config.h") {
+                       $output->write("$file: #include \"$header\": file not found\n");
+                   }
+
+                   if(defined($include)) {
+                       $includes{$include}++;
+                       foreach my $include (keys(%{$include2info{$include}{includes}})) {
+                           $includes{$include}++;
+                       }
+                   }
+               }
+
+               if($check_protection && $header) {
+                   if((-e "$wine_dir/include/$header" || -e "$wine_dir/$file_dir/$header")) {
+                       if($header !~ /^(oleauto\.h|win(?:base|def|error|gdi|nls|nt|user)\.h)$/ &&
+                          $file_dir !~ /tests$/)
+                       {
                            $output->write("$file: #include \<$header\> is a local include\n");
                        }
                    }
@@ -421,17 +572,18 @@ foreach my $file ($options->c_files) {
                    my $macro = uc($header);
                    $macro =~ y/\.\//__/;
                    $macro = "HAVE_" . $macro;
-                   
-                   if($nativeapi->is_conditional_header($header)) { 
+
+                   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");
-                               }
+                               # Do nothing X Windows is handled differently
                            } 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, " . 
+                               my $name = $1;
+                               if($header !~ /^alloca\.h$/ &&
+                                  $file_dir !~ /tests$/ &&
+                                  !$preprocessor->is_def("STATFS_DEFINED_BY_$name"))
+                               {
+                                   $output->write("$file: #$directive $argument: is a conditional include, " .
                                                   "but is not protected\n");
                                }
                            }
@@ -442,118 +594,92 @@ foreach my $file ($options->c_files) {
                    }
                }
 
-               if($check_local) {
+               if($check_local && $header) {
                    if(-e "$file_dir/$header") {
-                       $includes{"$file_dir/$header"}{used}++;
-                       foreach my $name (keys(%{$includes{"$file_dir/$header"}{includes}})) {
-                           $includes{$name}{used}++;
+                       if($file_dir ne ".") {
+                           $include2info{"$file_dir/$header"}{used}++;
+                           foreach my $name (keys(%{$include2info{"$file_dir/$header"}{includes}})) {
+                               $include2info{$name}{used}++;
+                           }
+                       } else {
+                           $include2info{"$header"}{used}++;
+                           foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
+                               $include2info{$name}{used}++;
+                           }
+                       }
+                   } elsif(-e "$file_dir/../$header") {
+                       if($file_dir =~ m%^(.*?)/[^/]+$%) {
+                           $include2info{"$1/$header"}{used}++;
+                           foreach my $name (keys(%{$include2info{"$1/$header"}{includes}})) {
+                               $include2info{$name}{used}++;
+                           }
+                       } else {
+                           $include2info{"$header"}{used}++;
+                           foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
+                               $include2info{$name}{used}++;
+                           }
+                       }
+                   } elsif($header =~ /^(?:kernel_private\.h)$/) { # FIXME: Kludge
+                       $include2info{"dlls/kernel/$header"}{used}++;
+                       foreach my $name (keys(%{$include2info{"dlls/kernel/$header"}{includes}})) {
+                           $include2info{$name}{used}++;
+                       }
+                   } elsif($header =~ /^(?:gdi_private\.h)$/) { # FIXME: Kludge
+                       $include2info{"dlls/gdi/$header"}{used}++;
+                       foreach my $name (keys(%{$include2info{"dlls/gdi/$header"}{includes}})) {
+                           $include2info{$name}{used}++;
+                       }
+                   } elsif($header =~ /^(?:ntdll_misc\.h)$/) { # FIXME: Kludge
+                       $include2info{"dlls/ntdll/$header"}{used}++;
+                       foreach my $name (keys(%{$include2info{"dlls/ntdll/$header"}{includes}})) {
+                           $include2info{$name}{used}++;
+                       }
+                   } elsif($header =~ /^(?:controls\.h|message\.h)$/) { # FIXME: Kludge
+                       $include2info{"dlls/user/$header"}{used}++;
+                       foreach my $name (keys(%{$include2info{"dlls/user/$header"}{includes}})) {
+                           $include2info{$name}{used}++;
+                       }
+                   } elsif($header =~ /^(?:ts_xlib\.h|winproc\.h|x11drv\.h|x11font\.h)$/) { # FIXME: Kludge
+                       $include2info{"dlls/x11drv/$header"}{used}++;
+                       foreach my $name (keys(%{$include2info{"dlls/x11drv/$header"}{includes}})) {
+                           $include2info{$name}{used}++;
                        }
                    } elsif(-e "$wine_dir/include/$header") {
-                       $includes{"include/$header"}{used}++;
-                       foreach my $name (keys(%{$includes{"include/$header"}{includes}})) {
-                           $includes{$name}{used}++;
+                       $include2info{"include/$header"}{used}++;
+                       foreach my $name (keys(%{$include2info{"include/$header"}{includes}})) {
+                           $include2info{$name}{used}++;
                        }
-                   } else {
+                   } elsif ($header ne "config.h") {
                        $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) {
+
+    &winapi_parser::parse_c_file($file, {
+       c_comment_found => $found_c_comment,
+       cplusplus_comment_found => $found_cplusplus_comment,
+       function_create => $create_function,
+       function_found => $found_function,
+       type_create => $create_type,
+       type_found => $found_type,
+       preprocessor_found => $found_preprocessor
+    });
+
+    if($options->config_unnecessary) {
        if($config && $conditional == 0) {
-           $output->write("$file: includes config.h but do not use any conditionals\n");
+           $output->write("$file: include2info config.h but do not use any conditionals\n");
        }
     }
 
-    winapi_local::check_file $options, $output, $file, \%functions;
+    &winapi_local::check_file($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;
+    &winapi_global::check_modules(\%complete_module, \%module2functions);
 
-           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 (sort(keys(%module_pseudo_stub_count16))) {
-               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($stubs) {
-                       $output->write("*.c: $module: $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 (sort(keys(%module_pseudo_stub_count32))) {
-               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($stubs) {
-                       $output->write("*.c: $module: $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");
-           }
-       }
+    if($all_modules) {
+       &winapi_global::check_all_modules(\%include2info);
     }
-
-    winapi_global::check $options, $output, $win16api, $nativeapi if $options->win16;
-    winapi_global::check $options, $output, $win32api, $nativeapi if $options->win32;
 }