- Implemented stub statistics. Turned off by default. (Requested by Francois
Gouget).
- Implemented missing prototype checking. Turned off by default (Requested
by Dimitry Timoshkov).
- Implemented .spec file name sanity checking. Turned off by default.
- Implemented documentation width checking. Turned off by default.
- Minor bug fixes.

diff --git a/tools/winapi_check/win32/advapi32.api b/tools/winapi_check/win32/advapi32.api
index 0387450..b1e1a48 100644
--- a/tools/winapi_check/win32/advapi32.api
+++ b/tools/winapi_check/win32/advapi32.api
@@ -19,6 +19,7 @@
 TOKEN_INFORMATION_CLASS
 ULONG
 WORD
+
 %long --extension
 
 LSA_HANDLE
@@ -36,6 +37,7 @@
 LPCWSTR *
 LPDWORD
 LPENUM_SERVICE_STATUSA
+LPENUM_SERVICE_STATUSW
 LPHANDLER_FUNCTION
 LPHKEY
 LPLONG
diff --git a/tools/winapi_check/win32/shell32.api b/tools/winapi_check/win32/shell32.api
index 0d43934..d4b0e93 100644
--- a/tools/winapi_check/win32/shell32.api
+++ b/tools/winapi_check/win32/shell32.api
@@ -22,7 +22,6 @@
 UINT
 ULONG
 WCHAR
-WORD
 WPARAM
 
 %long # --forbidden
diff --git a/tools/winapi_check/win32/shlwapi.api b/tools/winapi_check/win32/shlwapi.api
index 76290c4..e6dd239 100644
--- a/tools/winapi_check/win32/shlwapi.api
+++ b/tools/winapi_check/win32/shlwapi.api
@@ -10,7 +10,6 @@
 HWND
 UINT
 WCHAR
-WORD
 
 %long # --forbidden
 
diff --git a/tools/winapi_check/winapi.pm b/tools/winapi_check/winapi.pm
index 450ae7b..a755ba4 100644
--- a/tools/winapi_check/winapi.pm
+++ b/tools/winapi_check/winapi.pm
@@ -8,9 +8,11 @@
     my $self  = {};
     bless ($self, $class);
 
+    my $options = \${$self->{OPTIONS}};
     my $output = \${$self->{OUTPUT}};
     my $name = \${$self->{NAME}};
 
+    $$options = shift;
     $$output = shift;
     $$name = shift;
     my $path = shift;
@@ -143,13 +145,13 @@
 sub parse_spec_file {
     my $self = shift;
 
+    my $options = \${$self->{OPTIONS}};
     my $output = \${$self->{OUTPUT}};
     my $function_arguments = \%{$self->{FUNCTION_ARGUMENTS}};
     my $function_calling_convention = \%{$self->{FUNCTION_CALLING_CONVENTION}};
     my $function_stub = \%{$self->{FUNCTION_STUB}};
     my $function_module = \%{$self->{FUNCTION_MODULE}};
 
-
     my $file = shift;
 
     my %ordinals;
@@ -192,6 +194,38 @@
 	    } elsif($$function_module{$internal_name} !~ /$module/) {
 		$$function_module{$internal_name} .= " & $module";
 	    }
+
+	    if($$options->spec_mismatch) {
+		if($external_name eq "@") {
+		    if($internal_name !~ /^\U$module\E_$ordinal$/) {
+			$$output->write("$file: $external_name: the internal name ($internal_name) mismatch\n");
+		    }
+		} else {
+		    my $name = $external_name;
+
+		    my $name1 = $name;
+		    $name1 =~ s/^Zw/Nt/;
+
+		    my $name2 = $name;
+		    $name2 =~ s/^(?:_|Rtl|k32|K32)//;
+
+		    my $name3 = $name;
+		    $name3 =~ s/^INT_Int[0-9a-f]{2}Handler$/BUILTIN_DefaultIntHandler/;
+
+		    my $name4 = $name;
+		    $name4 =~ s/^(VxDCall)\d$/$1/;
+
+		    # FIXME: This special case is becuase of a very ugly kludge that should be fixed IMHO
+		    my $name5 = $name;
+		    $name5 =~ s/^(.*?16)_(.*?)$/$1_fn$2/;
+
+		    if(uc($internal_name) ne uc($external_name) &&
+		       $internal_name !~ /(\Q$name\E|\Q$name1\E|\Q$name2\E|\Q$name3\E|\Q$name4\E|\Q$name5\E)/)
+		    {
+			$$output->write("$file: $external_name: internal name ($internal_name) mismatch\n");
+		    }
+		}
+	    }
 	} elsif(/^(\d+|@)\s+stub\s+(\S+)$/) {
 	    my $external_name = $2;
 
@@ -374,6 +408,13 @@
     return sort(keys(%$function_calling_convention));
 }
 
+sub all_functions_stub {
+    my $self = shift;
+    my $function_stub = \%{$self->{FUNCTION_STUB}};
+
+    return sort(keys(%$function_stub));
+}
+
 sub all_functions_found {
     my $self = shift;
     my $function_found = \%{$self->{FUNCTION_FOUND}};
diff --git a/tools/winapi_check/winapi_check b/tools/winapi_check/winapi_check
index b05480c..86b5fd6 100755
--- a/tools/winapi_check/winapi_check
+++ b/tools/winapi_check/winapi_check
@@ -42,7 +42,7 @@
     import winapi_parser;
 }
 
-my $options = winapi_options->new(\@ARGV);
+my $options = winapi_options->new(\@ARGV, $wine_dir);
 if($options->help) {
     $options->show_help;
     exit;
@@ -50,8 +50,8 @@
 
 my $output = 'output'->new;
 
-my $win16api = 'winapi'->new($output, "win16", "$winapi_check_dir/win16");
-my $win32api = 'winapi'->new($output, "win32", "$winapi_check_dir/win32");
+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 $nativeapi = 'nativeapi'->new($output, "$winapi_check_dir/nativeapi.dat", "$wine_dir/configure.in", "$wine_dir/include/config.h.in");
@@ -108,11 +108,51 @@
     }    
 }
 
+my %declared_functions;
+
 my $progress_output;
 my $progress_current=0;
-my $progress_max=scalar($options->files);
-foreach my $file ($options->files) {
-    my %functions;
+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 = ();
 
     $progress_current++;
     if($options->progress) {
@@ -187,7 +227,18 @@
 	    };
 	    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,
@@ -235,16 +286,28 @@
 		    }
 		}
 	    }
+
+	    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->documentation && (defined($module16) || defined($module32)) && 
 	       $linkage ne "extern" && $statements ne "") 
 	    {		
 		my $name1;
 		my $name2;
-		my $name3;
-		my $name4;
-		my $name5;
-
+		
 		if(defined($module16) && !defined($module32)) {
 		    my @uc_modules16 = split(/\s*\&\s*/, uc($module16));
 		    push @uc_modules16, "WIN16";
@@ -254,17 +317,9 @@
 			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/([AW])$/16$1/;
-
-		    $name3 = $name1;
-		    $name3 =~ s/16(([AW])?)$/$1/;
-
-		    $name4 = $name1;
-		    # $name4 =~ s/^(.*?)(?:16)?$/\U$1\E/;
-
-		    $name5 = $name1;
-		    $name5 = s/^(.*?)16_fn(.*?)$/$116_$2/;
+		    $name2 = s/^(.*?)16_fn(.*?)$/$116_$2/;
 		} elsif(!defined($module16) && defined($module32)) {
 		    my @uc_modules32 = split(/\s*\&\s*/, uc($module32));
 
@@ -274,15 +329,7 @@
 		    }
 
 		    $name2 = $name1;
-		    # $name2 =~ s/([AW])$/32$1/;
-
-		    $name3 = $name1;
-		    # $name3 =~ s/32(([AW])?)$/$1/;
-
-		    $name4 = $name1;
-		    $name4 =~ s/AW$//;
-
-		    $name5 = $name1;
+		    $name2 =~ s/AW$//;
 		} else {
 		    my @uc_modules = split(/\s*\&\s*/, uc($module16));
 		    push @uc_modules, split(/\s*\&\s*/, uc($module32));
@@ -293,20 +340,24 @@
 		    }
 
 		    $name2 = $name1;
-
-		    $name3 = $name1;
-
-		    $name4 = $name1;
-
-		    $name5 = $name1;
 		}
 
-		if($name !~ /^SMapLS|SUnMapLS/ && $documentation !~ /\b($name|$name1|$name2|$name3|$name4|$name5)\b/) {
+		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");
+			}
+		    }
+		}
+	    }
 	}
     };
 
@@ -424,10 +475,81 @@
 $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 (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("$name: include file is never used\n");
+		$output->write("*.c: $name: include file is never used\n");
 	    }
 	}
     }
@@ -435,4 +557,3 @@
     winapi_global::check $options, $output, $win16api, $nativeapi if $options->win16;
     winapi_global::check $options, $output, $win32api, $nativeapi if $options->win32;
 }
-
diff --git a/tools/winapi_check/winapi_options.pm b/tools/winapi_check/winapi_options.pm
index 3c326cb..59e0849 100644
--- a/tools/winapi_check/winapi_options.pm
+++ b/tools/winapi_check/winapi_options.pm
@@ -34,6 +34,8 @@
     "config" => { default => 1, description => "check configuration include consistancy" },
     "config-unnessary" => { default => 0, parent => "config", description => "check for unnessary #include \"config.h\"" },
 
+    "spec-mismatch" => { default => 0, description => "spec file mismatch checking" },
+
     "local" =>  { default => 1, description => "local checking" },
     "module" => { 
 	default => { active => 1, filter => 0, hash => {} },
@@ -60,12 +62,15 @@
     "misplaced" => { default => 0, parent => "local", description => "check for misplaced functions" },
     "cross-call" => { default => 0, parent => "local", description => "check for cross calling functions" },
     "documentation" => { default => 1, parent => "local", description => "check for documentation inconsistances\n" },
-             
-    "global" => { default => 1, description => "global checking" }, 
+    "documentation-width" => { default => 0, parent => "documentation", description => "check for documentation width inconsistances\n" },
+
+    "global" => { default => 1, description => "global checking" },
     "declared" => { default => 1, parent => "global", description => "declared checking" }, 
     "implemented" => { default => 1, parent => "global", description => "implemented checking" },
     "implemented-win32" => { default => 0, parent => "implemented", description => "implemented as win32 checking" },
-    "include" => { default => 1, parent => "global", description => "include checking" }
+    "include" => { default => 1, parent => "global", description => "include checking" },
+    "headers" => { default => 0, parent => "global", description => "headers checking" },
+    "stubs" => { default => 0, parent => "global", description => "stubs checking" }
 );
 
 my %short_options = (
@@ -82,6 +87,7 @@
 
     my $refarguments = shift;
     my @ARGV = @$refarguments;
+    my $wine_dir = shift;
 
     for my $name (sort(keys(%options))) {
         my $option = $options{$name};
@@ -92,7 +98,8 @@
 	$$refvalue = $$option{default};
     }
 
-    my $files = \@{$self->{FILES}};
+    my $c_files = \@{$self->{C_FILES}};
+    my $h_files = \@{$self->{H_FILES}};
     my $module = \${$self->{MODULE}};
     my $global = \${$self->{GLOBAL}};
 
@@ -161,26 +168,33 @@
 	    print STDERR "<internal>: usage: winapi-check [--help] [<files>]\n";
 	    exit 1;
 	} else {
-	    push @$files, $_;
+	    push @$c_files, $_;
 	}
     }
 
-    my $paths;
-    if($#$files == -1) {
-	$paths = ".";
+    my $c_paths;
+    if($#$c_files == -1) {
+	$c_paths = ".";
 	$$global = 1;
     } else {
-	$paths = join(" ",@$files);
+	$c_paths = join(" ", @$c_files);
     }
 
-    @$files = sort(map {
+    my $h_paths = "$wine_dir/include $wine_dir/include/wine";
+
+    @$c_files = sort(map {
 	s/^.\/(.*)$/$1/;
 	if(!/spec\.c$/) {
 	    $_;
 	} else {
 	    ();
 	}
-    } split(/\n/, `find $paths -name \\*.c`));
+    } split(/\n/, `find $c_paths -name \\*.c`));
+
+    @$h_files = sort(map {
+	s/^.\/(.*)$/$1/;
+	$_;
+    } split(/\n/, `find $h_paths -name \\*.h`));
 
     return $self;
 }
@@ -250,7 +264,9 @@
     return $$refvalue;
 }
 
-sub files { my $self = shift; return @{$self->{FILES}}; }
+sub c_files { my $self = shift; return @{$self->{C_FILES}}; }
+
+sub h_files { my $self = shift; return @{$self->{H_FILES}}; }
 
 sub report_module {
     my $self = shift;
diff --git a/tools/winapi_check/winapi_parser.pm b/tools/winapi_check/winapi_parser.pm
index e8b263c..2c21eb3 100644
--- a/tools/winapi_check/winapi_parser.pm
+++ b/tools/winapi_check/winapi_parser.pm
@@ -36,6 +36,7 @@
     my %regs_entrypoints;
     my @comments = ();
     my $level = 0;
+    my $extern_c = 0;
     my $again = 0;
     my $lookahead = 0;
     my $lookahead_count = 0;
@@ -55,7 +56,8 @@
 		$lookahead_count = 0;
 	    }
 	    $lookahead_count++;
-	    print " $level: $line\n" if $options->debug >= 2;
+	    print " $level($lookahead_count): $line\n" if $options->debug >= 2;
+	    print "*** $_\n" if $options->debug >= 3;
 	} else {
 	    $lookahead_count = 0;
 	    $again = 0;
@@ -88,11 +90,17 @@
 		} else {
 		    &$preprocessor_found_callback($1, "");
 		}
-		$again = 1;
 		next;
 	    }
 	}
 
+	# Remove extern "C"
+	if(s/^\s*extern\s+"C"\s+\{//m) { 
+	    $extern_c = 1;
+	    $again = 1;
+	    next; 
+	}
+
 	my $documentation; 
 	{
 	    my $n = $#comments;
@@ -157,6 +165,10 @@
 		$line .= "}" if $level > 1;
 		print "-1: \}$_\n" if $options->debug >= 2; 
 		$level--;
+		if($level == -1 && $extern_c) {
+		    $extern_c = 0;
+		    $level = 0;
+		}
 	    }
 
 	    if($line !~ /^\s*$/) {
@@ -166,7 +178,7 @@
 	    if($function && $level == 0) {
 		&$function_end;
 	    }
-	    next;
+	    next;	    
 	} elsif(/(extern\s+|static\s+)?((struct\s+|union\s+|enum\s+)?\w+((\s*\*)+\s*|\s+))((__cdecl|__stdcall|VFWAPIV|VFWAPI|WINAPIV|WINAPI)\s+)?(\w+(\(\w+\))?)\s*\(([^\)]*)\)\s*(\{|\;)/s) {
 	    $_ = $'; $again = 1;
 	    
@@ -300,6 +312,8 @@
 	    $_ = $'; $again = 1;
 	} elsif(/;/s) {
 	    $_ = $'; $again = 1;
+	} elsif(/extern\s+"C"\s+{/s) {
+	    $_ = $'; $again = 1;
         } elsif(/\{/s) {
             $_ = $'; $again = 1;
             print "+1: $_\n" if $options->debug >= 2;