Make winelauncher work better for source tree builds.
[wine] / tools / winapi_check / winapi_check
1 #!/usr/bin/perl -w
2
3 # Copyright 1999-2000 Patrik Stridvall
4
5 # Note that winapi_check are using heuristics quite heavily.
6 # So always remember that:
7 #
8 # "Heuristics are bug ridden by definition. 
9 #  If they didn't have bugs, then they'd be algorithms."
10 #
11 # In other words, reported bugs are only potential bugs not
12 # real bugs, so they are called issues rather than bugs.
13 #
14
15 use strict;
16
17 my $wine_dir;
18 my $winapi_check_dir;
19
20 BEGIN {
21
22     if($0 =~ /^((.*?)\/?tools\/winapi_check)\/winapi_check$/)
23     {
24         $winapi_check_dir = $1;
25         if($2 ne "")
26         {
27             $wine_dir = $2;
28         } else {
29             $wine_dir = ".";
30         }
31     }
32     @INC = ($winapi_check_dir);
33
34     require "modules.pm";
35     require "nativeapi.pm";
36     require "output.pm";
37     require "preprocessor.pm";
38     require "winapi.pm";
39     require "winapi_function.pm";
40     require "winapi_local.pm";
41     require "winapi_global.pm";
42     require "winapi_options.pm";
43     require "winapi_parser.pm";
44
45     import modules;
46     import nativeapi;
47     import output;
48     import preprocessor;
49     import winapi;
50     import winapi_function;
51     import winapi_local;
52     import winapi_global;
53     import winapi_options;
54     import winapi_parser;
55 }
56
57 my $current_dir = ".";
58 if(length($wine_dir) != 1) {
59     my $pwd; chomp($pwd = `pwd`);
60     foreach my $n (1..((length($wine_dir) + 1) / 3)) {
61         $pwd =~ s/\/([^\/]*)$//;
62         $current_dir = "$1/$current_dir";
63     }
64     $current_dir =~ s/\/.$//;
65 }
66
67 my $output = 'output'->new;
68
69 my $options = winapi_options->new($output, \@ARGV, $wine_dir);
70 if(!defined($options)) {
71     $output->write("usage: winapi_check [--help] [<files>]\n");
72
73     exit 1;
74 } elsif($options->help) {
75     $options->show_help;
76     exit;
77 }
78
79 sub file_type {
80     my $file = shift;
81
82     my $file_dir = $file;
83     if(!($file_dir =~ s/^(.*?)\/[^\/]*$/$1/)) {
84         $file_dir = ".";
85     }
86     
87     $file_dir =~ s/^$wine_dir\///;
88
89     if($file_dir =~ /^(libtest|program|rc|tests|tools)/ || 
90        $file =~ /dbgmain\.c$/ ||
91        $file =~ /wineclipsrv\.c$/) # FIXME: Kludge
92     {
93         return "application";
94     } elsif($file_dir =~ /^(debug|miscemu)/) {
95         return "emulator";
96     } else {
97         return "library";
98     }
99 }
100
101 my $modules = 'modules'->new($options, $output, $wine_dir, $current_dir, \&file_type, "$winapi_check_dir/modules.dat");
102
103 my $win16api = 'winapi'->new($options, $output, "win16", "$winapi_check_dir/win16");
104 my $win32api = 'winapi'->new($options, $output, "win32", "$winapi_check_dir/win32");
105 my @winapis = ($win16api, $win32api);
106
107 if($options->global) {
108     'winapi'->read_all_spec_files($modules, $wine_dir, $current_dir, \&file_type, $win16api, $win32api);
109 } else {
110     my @spec_files = $modules->allowed_spec_files($wine_dir, $current_dir);
111     'winapi'->read_spec_files($modules, $wine_dir, $current_dir, \@spec_files, $win16api, $win32api);
112 }
113
114 my $nativeapi = 'nativeapi'->new($options, $output, "$winapi_check_dir/nativeapi.dat", "$wine_dir/configure.in", "$wine_dir/include/config.h.in");
115
116 for my $name ($win32api->all_functions) {
117     my $module16 = $win16api->function_module($name);
118     my $module32 = $win32api->function_module($name);
119         
120     if(defined($module16)) {
121         $win16api->found_shared_function($name);
122         $win32api->found_shared_function($name);
123         
124         if($options->shared) {
125             $output->write("*.spec: $name: is shared between $module16 (Win16) and $module32 (Win32)\n");
126         }
127     }
128 }
129
130 my %includes;
131 {   
132     my @files = map {
133         s/^.\/(.*)$/$1/;
134         $_; 
135     } split(/\n/, `find . -name \\*.h`);
136     
137     foreach my $file (@files) {
138         my $file_dir = $file;
139         if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
140             $file_dir = ".";
141         }
142    
143         $includes{$file} = { name => $file };
144
145         open(IN, "< $file");
146         while(<IN>) {
147             if(/^\s*\#\s*include\s*\"(.*?)\"/) {
148                 my $header = $1;
149                 if(-e "$file_dir/$header") {
150                     $includes{$file}{includes}{"$file_dir/$header"}++;
151                 } elsif(-e "$wine_dir/include/$header") {
152                     $includes{$file}{includes}{"include/$header"}++;
153                 } else {                   
154                     $output->write("$file: #include \"$header\" is not a local include\n");
155                 }
156             }
157         }
158         close(IN);
159     }
160
161     my @files2 = ("acconfig.h", "poppack.h", "pshpack1.h", "pshpack2.h", "pshpack4.h", "pshpack8.h",
162                   "storage.h", "ver.h");
163     foreach my $file2 (@files2) {
164         $includes{"include/$file2"}{used}++;
165     }    
166 }
167
168 my %declared_functions;
169
170 my $progress_output;
171 my $progress_current=0;
172 my $progress_max=scalar($options->c_files);
173
174 if($options->headers) {
175     $progress_max += scalar($options->h_files);
176
177     foreach my $file ($options->h_files) {
178         my %functions;
179         
180         $progress_current++;
181         if($options->progress) {
182             $output->progress("$file: file $progress_current of $progress_max");
183         }
184         
185         my $found_function = sub {
186             my $documentation = shift;
187             my $linkage = shift;
188             my $return_type = shift;
189             my $calling_convention = shift;
190             my $internal_name = shift;
191             my $refargument_types = shift;
192             my @argument_types = @$refargument_types;
193             my $refargument_names = shift;
194             my @argument_names = @$refargument_names;
195             my $refargument_documentations = shift;
196             my @argument_documentations = @$refargument_documentations;
197             my $statements = shift;
198
199             foreach my $winapi (@winapis) {
200                 my $module = $winapi->function_module($internal_name);
201                 if(!defined($module)) { next }
202
203                 my $external_name = $winapi->function_external_name($internal_name);
204                 # FIXME: Kludge because of the THUNK variants
205                 if(!defined($external_name)) {
206                     next;
207                 }
208
209                 my $output_function = sub {
210                     my $message = shift;
211
212                     $output->write("$file: $module: $return_type ");
213                     $output->write("$calling_convention ") if $calling_convention;
214                     $output->write("$internal_name(" . join(",", @argument_types) . "): $message\n");
215                 };
216                 
217                 if(!defined($declared_functions{$winapi->name}{$external_name})) {
218                     $declared_functions{$winapi->name}{$external_name} = "$file";
219                 } elsif($options->headers_duplicated) {
220                     my $message = "declared more than once";
221                     if($file ne $declared_functions{$winapi->name}{$external_name}) {
222                         $message .= ", first declaration in '" . $declared_functions{$winapi->name}{$external_name} . "'";
223                     }
224                     &$output_function("$message");
225                 }
226
227                 if($options->headers_misplaced) {
228                     if($file =~ /^include\/[^\/]*$/ && $winapi->name eq "win16") {
229                         &$output_function("declaration misplaced");
230                     } elsif($file =~ /^include\/wine\/[^\/]*$/ && $winapi->name eq "win32") {
231                         &$output_function("declaration misplaced");
232                     }
233                 }
234             }
235         };
236         
237         my $found_preprocessor = sub {
238             my $directive = shift;
239             my $argument = shift;
240         };
241         
242         winapi_parser::parse_c_file $options, $output, $file, $found_function, $found_preprocessor;
243     }
244 }
245
246 my %comment_width;
247 my %module_pseudo_stub_count16;
248 my %module_pseudo_stub_count32;
249
250 foreach my $file ($options->c_files) {
251     my %functions = ();
252
253     my $file_module16 = $modules->allowed_modules_in_file("$current_dir/$file");
254     my $file_module32 = $modules->allowed_modules_in_file("$current_dir/$file");
255
256     $progress_current++;
257     if($options->progress) {
258         $output->progress("$file: file $progress_current of $progress_max");
259     }
260
261     my $file_dir = $file;
262     if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
263         $file_dir = ".";
264     }
265    
266     my $file_type = file_type($file);
267     
268     my $found_function = sub {
269         my $documentation = shift;
270         my $linkage = shift;
271         my $return_type = shift;
272         my $calling_convention = shift;
273         my $internal_name = shift;
274         my $refargument_types = shift;
275         my @argument_types = @$refargument_types;
276         my $refargument_names = shift;
277         my @argument_names = @$refargument_names;
278         my $refargument_documentations = shift;
279         my @argument_documentations = @$refargument_documentations;
280         my $statements = shift;
281
282         my $external_name16 = $win16api->function_external_name($internal_name);
283         my $external_name32 = $win32api->function_external_name($internal_name);
284
285         if($options->global) {
286             $win16api->found_type($return_type) if $options->win16;
287             $win32api->found_type($return_type) if $options->win32;
288             for my $argument (@argument_types) {
289                 $win16api->found_type($argument) if $options->win16;
290                 $win32api->found_type($argument) if $options->win32;
291             }
292             
293             $win16api->found_function($internal_name) if $options->win16;
294             $win32api->found_function($internal_name) if $options->win32;
295         }
296         
297         if($file_type ne "application") {
298             my $module16 = $win16api->function_module($internal_name);
299             my $module32 = $win32api->function_module($internal_name);
300
301             if(defined($module16)) {
302                 $modules->found_module_in_dir($module16, $file_dir);
303             }
304             if(defined($module32)) {
305                 $modules->found_module_in_dir($module32, $file_dir);
306             }
307
308             my $function = 'winapi_function'->new;
309             $functions{$internal_name} = $function;
310            
311             $function->documentation($documentation);
312             $function->linkage($linkage);
313             $function->file($file);
314             $function->return_type($return_type); 
315             $function->calling_convention($calling_convention);
316             $function->external_name16($external_name16);
317             $function->external_name32($external_name32);
318             $function->internal_name($internal_name);
319             $function->argument_types([@argument_types]);
320             $function->argument_names([@argument_names]);
321             $function->statements($statements);
322             $function->module16($module16);
323             $function->module32($module32);
324
325             my $prefix = "";
326             $prefix .= "$file: ";
327             if(defined($module16) && !defined($module32)) {
328                 $prefix .= "$module16: ";
329             } elsif(!defined($module16) && defined($module32)) {
330                 $prefix .= "$module32: ";
331             } elsif(defined($module16) && defined($module32)) {
332                 $prefix .= "$module16 & $module32: ";
333             } else {
334                 $prefix .= "<>: ";
335             }
336             $prefix .= "$return_type ";
337             $prefix .= "$calling_convention " if $calling_convention;
338             $prefix .= "$internal_name(" . join(",", @argument_types) . "): ";
339             $output->prefix($prefix);
340
341             if($options->local && $options->misplaced &&
342                $linkage ne "extern" && $statements) 
343             {
344                 if($options->win16 && $options->report_module($module16)) {
345                     my $match = 0;
346                     foreach my $module (split(/ & /, $module16)) {
347                         foreach my $file_module (split(/ & /, $file_module16)) {
348                             if($module eq $file_module) {
349                                 $match++;
350                             }
351                         }
352                     }
353                     if(!$match && $file ne "library/port.c" && !$nativeapi->is_function($internal_name)) {
354                         $output->write("is misplaced\n");
355                     }
356                 }
357
358                 if($options->win32 && $options->report_module($module32)) {
359                     my $match = 0;
360                     foreach my $module (split(/ & /, $module32)) {
361                         foreach my $file_module (split(/ & /, $file_module32)) {
362                             if($module eq $file_module) {
363                                 $match++;
364                             }
365                         }
366                     }
367                     if(!$match && $file ne "library/port.c" && !$nativeapi->is_function($internal_name)) {
368                         $output->write("is misplaced\n");
369                     }
370                 }
371             }
372
373             if($options->local && $options->headers && $options->prototype) {
374                 if($options->win16 && $options->report_module($module16)) {
375                     if(!defined($external_name16) || (!$nativeapi->is_function($external_name16) && 
376                        !defined($declared_functions{$win16api->name}{$external_name16})))
377                     {
378                         if(!defined($external_name16) || ($external_name16 !~ /^DllEntryPoint$/ &&
379                             $internal_name !~ /^I(?:Malloc|Storage)16_fn/ &&
380                             $internal_name !~ /^(?:\Q$module16\E|THUNK|WIN16)_\Q$external_name16\E(?:16)?$/))
381                         {
382                             $output->write("no prototype\n");
383                         }
384                     }
385                 }
386
387                 if($options->win32 && $options->report_module($module32)) {
388                     if(!defined($external_name32) || (!$nativeapi->is_function($external_name32) &&                                                       !defined($declared_functions{$win32api->name}{$external_name32})))
389                     {
390                         if(!defined($external_name32) || ($external_name32 !~ /^Dll(?:
391                            Install|CanUnloadNow|GetClassObject|GetVersion|
392                            RegisterServer|RegisterServerEx|UnregisterServer)|DriverProc$/x && 
393                            $internal_name !~ /^COMCTL32_Str/ &&
394                            $internal_name !~ /^(?:\Q$module32\E|wine)_(?:\Q$external_name32\E|\d+)$/))
395                         {
396                             $output->write("no prototype\n");
397                         }
398                     }
399                 }
400             }
401
402             if($options->local && $options->argument) {
403                 if($options->win16 && $options->report_module($module16)) {
404                   winapi_local::check_function $options, $output,
405                     $return_type, $calling_convention, $external_name16, $internal_name, [@argument_types], $nativeapi, $win16api;
406                 }       
407                 if($options->win32 && $options->report_module($module32)) {
408                   winapi_local::check_function $options, $output,
409                     $return_type, $calling_convention, $external_name32, $internal_name, [@argument_types], $nativeapi, $win32api;
410                 }
411             }
412
413             if($options->local && $options->statements) {
414                 if($options->win16 && $options->report_module($module16)) {
415                   winapi_local::check_statements $options, $output, $win16api, \%functions, $function;
416                 }
417
418                 if($options->win32 && $options->report_module($module32)) {
419                   winapi_local::check_statements $options, $output, $win32api, \%functions, $function;
420                 }
421             }
422
423             if($options->stubs) {
424                 if(defined($statements) && $statements =~ /FIXME[^;]*stub/) {
425                     if($options->win16 && $options->report_module($module16)) {
426                         foreach my $module (split(/ \& /, $module16)) {
427                             $module_pseudo_stub_count16{$module}++;
428                         }
429                     }
430                     if($options->win32 && $options->report_module($module32)) {
431                         foreach my $module (split(/ \& /, $module32)) {
432                             $module_pseudo_stub_count32{$module}++;
433                         }
434                     }
435                 }
436             }
437             
438             if($options->local && $options->documentation &&
439                (defined($module16) || defined($module32)) &&
440                $linkage ne "extern" && $statements)
441             {
442                 my $name1;
443                 my $name2;
444                 
445                 if(defined($module16) && !defined($module32)) {
446                     my @uc_modules16 = split(/\s*\&\s*/, uc($module16));
447                     push @uc_modules16, "WIN16";
448
449                     $name1 = $internal_name;
450                     foreach my $uc_module16 (@uc_modules16) {
451                         if($name1 =~ s/^$uc_module16\_//) { last; }
452                     }
453
454                     # FIXME: This special case is becuase of a very ugly kludge that should be fixed IMHO
455                     $name2 = $name1;
456                     $name2 = s/^(.*?)16_fn(.*?)$/$116_$2/;
457                 } elsif(!defined($module16) && defined($module32)) {
458                     my @uc_modules32 = split(/\s*\&\s*/, uc($module32));
459                     push @uc_modules32, "wine";
460
461                     foreach my $uc_module32 (@uc_modules32) {
462                         if($uc_module32 =~ /^WS2_32$/) {
463                             push @uc_modules32, "WSOCK32"; 
464                         }
465                     }
466
467                     $name1 = $internal_name;
468                     foreach my $uc_module32 (@uc_modules32) {
469                         if($name1 =~ s/^$uc_module32\_//) { last; }
470                     }
471
472                     $name2 = $name1;
473                     $name2 =~ s/AW$//;
474                 } else {
475                     my @uc_modules = split(/\s*\&\s*/, uc($module16));
476                     push @uc_modules, split(/\s*\&\s*/, uc($module32));
477
478                     $name1 = $internal_name;
479                     foreach my $uc_module (@uc_modules) {
480                         if($name1 =~ s/^$uc_module\_//) { last; }
481                     }
482
483                     $name2 = $name1;
484                 }
485
486                 if($documentation !~ /\b($internal_name|$name1|$name2)\b/) {
487                     $output->write("documentation: \\\n$documentation\n");
488                 }
489
490                 if($options->documentation_width) {             
491                     if($documentation =~ /(\/\**)/) {
492                         my $width = length($1);
493                         
494                         $comment_width{$width}++;
495                         if($width <= 65 || $width >= 81) {
496                             $output->write("comment is $width columns wide\n");
497                         }
498                     }
499                 }
500                 
501                 if($options->documentation_arguments) {
502                     my $n = 0;
503                     for my $argument_documentation (@argument_documentations) {
504                         $n++;
505                         if($argument_documentation ne "") {
506                             if($argument_documentation !~ /^\/\*\s+\[(?:in|out|in\/out)\].*?\*\/$/) {
507                                 $output->write("argument $n documentation: \\\n$argument_documentation\n");
508                             }
509                         }
510                     }
511                 }
512             }
513             $output->prefix("");
514         }
515     };
516
517     my $config = 0;
518     my $conditional = 0;
519     my $found_include = sub {
520         local $_ = shift;
521         if(/^\"config\.h\"/) {
522             $config++;
523         }
524     };
525     my $found_conditional = sub {
526         local $_ = shift;
527
528         $nativeapi->found_conditional($_);
529
530         if($options->config) {
531             if($file_type ne "application") {
532                 if(!$nativeapi->is_conditional($_)) {
533                     if(/^HAVE_/ && !/^HAVE_(IPX|MESAGL|BUGGY_MESAGL|WINE_CONSTRUCTOR)$/)
534                     {
535                         $output->write("$file: $_ is not declared as a conditional\n");
536                     }
537                 } else {
538                     $conditional++;
539                     if(!$config) {
540                         $output->write("$file: conditional $_ used but config.h is not included\n");
541                     }
542                 }
543             }
544         }
545     };
546     my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
547     my $found_preprocessor = sub {
548         my $directive = shift;
549         my $argument = shift;
550
551         $preprocessor->directive($directive, $argument);
552
553         if($options->config) {
554             if($directive eq "include") {
555                 my $header;
556                 my $check_protection;
557                 my $check_local;
558                 if($argument =~ /^<(.*?)>$/) {
559                    $header = $1;
560                    if($file_type ne "application") {
561                        $check_protection = 1;
562                    } else {
563                        $check_protection = 0;
564                    }
565                    $check_local = 0;
566                 } elsif($argument =~ /^"(.*?)"$/) {
567                    $header = $1;
568                    $check_protection = 0;
569                    $check_local = 1;
570                 }
571
572                 if($check_protection) {
573                     if((-e "$wine_dir/include/$header" || -e "$file_dir/$header")) {
574                         if($header !~ /^ctype.h$/) {
575                             $output->write("$file: #include \<$header\> is a local include\n");
576                         }
577                     }
578
579                     my $macro = uc($header);
580                     $macro =~ y/\.\//__/;
581                     $macro = "HAVE_" . $macro;
582                     
583                     if($nativeapi->is_conditional_header($header)) { 
584                         if(!$preprocessor->is_def($macro)) {
585                             if($macro =~ /^HAVE_X11/) {
586                                 # Do nothing X Windows is handled differently
587                             } elsif($macro =~ /^HAVE_(.*?)_H$/) {
588                                 if($header ne "alloca.h" && !$preprocessor->is_def("STATFS_DEFINED_BY_$1")) {
589                                     $output->write("$file: #$directive $argument: is a conditional include, " . 
590                                                    "but is not protected\n");
591                                 }
592                             }
593                         }
594                     } elsif($preprocessor->is_def($macro)) {
595                         $output->write("$file: #$directive $argument: is protected, " .
596                                        "but is not a conditional include\n");
597                     }
598                 }
599
600                 if($check_local) {
601                     if(-e "$file_dir/$header") {
602                         $includes{"$file_dir/$header"}{used}++;
603                         foreach my $name (keys(%{$includes{"$file_dir/$header"}{includes}})) {
604                             $includes{$name}{used}++;
605                         }
606                     } elsif(-e "$file_dir/../$header") { # FIXME: Kludge
607                         $includes{"$file_dir/../$header"}{used}++; # FIXME: This is not correct
608                         foreach my $name (keys(%{$includes{"$file_dir/../$header"}{includes}})) { # FIXME: This is not correct
609                             $includes{$name}{used}++;
610                         }
611                     } elsif(-e "$wine_dir/include/$header") {
612                         $includes{"include/$header"}{used}++;
613                         foreach my $name (keys(%{$includes{"include/$header"}{includes}})) {
614                             $includes{$name}{used}++;
615                         }
616                     } else {
617                         $output->write("$file: #include \"$header\" is not a local include\n");
618                     }
619                 }
620             }
621         }
622     };
623   
624     winapi_parser::parse_c_file $options, $output, $file, $found_function, $found_preprocessor;
625     
626     if($options->config_unnessary) {
627         if($config && $conditional == 0) {
628             $output->write("$file: includes config.h but do not use any conditionals\n");
629         }
630     }
631
632     winapi_local::check_file $options, $output, $file, \%functions;
633 }
634
635 $output->hide_progress;
636
637 if($options->global) {
638     if($options->documentation_width) {
639         foreach my $width (sort(keys(%comment_width))) {
640             my $count = $comment_width{$width};
641             $output->write("*.c: $count functions have comments of width $width\n");
642         }
643     }
644
645     if($options->stubs) {
646         if($options->win16) {
647             my %module_stub_count16;
648             my %module_total_count16;
649
650             foreach my $name ($win16api->all_functions,$win16api->all_functions_stub) {
651                 foreach my $module (split(/ \& /, $win16api->function_module($name))) {
652                     if($win16api->function_stub($name)) {
653                         $module_stub_count16{$module}++;
654                     }
655                     $module_total_count16{$module}++;
656                 }
657             }
658
659             foreach my $module ($win16api->all_modules) {
660                 if($options->report_module($module)) {
661                     my $real_stubs = $module_stub_count16{$module};
662                     my $pseudo_stubs = $module_pseudo_stub_count16{$module};
663
664                     if(!defined($real_stubs)) { $real_stubs = 0; }
665                     if(!defined($pseudo_stubs)) { $pseudo_stubs = 0; }
666
667                     my $stubs = $real_stubs + $pseudo_stubs;
668                     my $total = $module_total_count16{$module};
669
670                     if(!defined($total)) { $total = 0;}
671
672                     $output->write("*.c: $module: ");
673                     $output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo)\n");
674                 }
675             }
676         }
677         
678         if($options->win32) {
679             my %module_stub_count32;
680             my %module_total_count32;
681
682             foreach my $name ($win32api->all_functions,$win32api->all_functions_stub) {
683                 foreach my $module (split(/ \& /, $win32api->function_module($name))) {
684                     if($win32api->function_stub($name)) {
685                         $module_stub_count32{$module}++;
686                     }
687                     $module_total_count32{$module}++;
688                 }
689             }
690
691             foreach my $module ($win32api->all_modules) {
692                 if($options->report_module($module)) {
693                     my $real_stubs = $module_stub_count32{$module};
694                     my $pseudo_stubs = $module_pseudo_stub_count32{$module};
695
696                     if(!defined($real_stubs)) { $real_stubs = 0; }
697                     if(!defined($pseudo_stubs)) { $pseudo_stubs = 0; }
698
699                     my $stubs = $real_stubs + $pseudo_stubs;
700                     my $total = $module_total_count32{$module};
701
702                     if(!defined($total)) { $total = 0;}
703
704                     $output->write("*.c: $module: ");
705                     $output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo)\n");
706                 }
707             }
708         }
709     }
710
711     if($options->headers) {
712         foreach my $name (sort(keys(%includes))) {
713             if(!$includes{$name}{used}) {
714                 if($options->include) {
715                     $output->write("*.c: $name: include file is never used\n");
716                 }
717             }
718         }
719     }
720
721     winapi_global::check $options, $output, $win16api, $nativeapi if $options->win16;
722     winapi_global::check $options, $output, $win32api, $nativeapi if $options->win32;
723
724     $modules->global_report;
725     $nativeapi->global_report;
726 }