Changed the GDI driver interface to pass an opaque PHYSDEV pointer
[wine] / tools / winapi_check / winapi_check
1 #!/usr/bin/perl -w
2
3 # Copyright 1999-2001 Patrik Stridvall
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 #
19
20 # Note that winapi_check are using heuristics quite heavily.
21 # So always remember that:
22 #
23 # "Heuristics are bug ridden by definition. 
24 #  If they didn't have bugs, then they'd be algorithms."
25 #
26 # In other words, reported bugs are only potential bugs not
27 # real bugs, so they are called issues rather than bugs.
28 #
29
30 use strict;
31
32 BEGIN {
33     $0 =~ m%^(.*?/?tools)/winapi_check/winapi_check$%;
34     require "$1/winapi/setup.pm";
35 }
36
37 use config qw(
38     &files_filter &files_skip
39     &get_h_files
40     $current_dir $wine_dir
41 );
42 use output qw($output);
43 use winapi_check_options qw($options);
44
45 if($options->progress) {
46     $output->enable_progress;
47 } else {
48     $output->disable_progress;
49 }
50
51 use modules qw($modules);
52 use nativeapi qw($nativeapi);
53 use winapi qw($win16api $win32api @winapis);
54
55 use preprocessor;
56 use type;
57 use util qw(&is_subset);
58 use winapi_documentation;
59 use winapi_function;
60 use winapi_local;
61 use winapi_global;
62 use winapi_parser;
63
64 my %declared_functions;
65
66 my %include2info;
67 {   
68     my @files = get_h_files("winelib");
69
70     my $progress_current = 0;
71     my $progress_max = scalar(@files);   
72
73     foreach my $file (@files) {
74         $progress_current++;
75         $output->lazy_progress("$file: file $progress_current of $progress_max");
76
77         my $file_dir = $file;
78         if(!($file_dir =~ s%(.*?)/[^/]+$%$1%)) {
79             $file_dir = ".";
80         }
81
82         $include2info{$file} = { name => $file };
83
84         open(IN, "< $wine_dir/$file");
85         while(<IN>) {
86             if(/^\s*\#\s*include\s*\"(.*?)\"/) {
87                 my $header = $1;
88                 if(-e "$wine_dir/$file_dir/$header") {
89                     $include2info{$file}{includes}{"$file_dir/$header"}++;
90                 } elsif(-e "$wine_dir/$file_dir/../$header") {
91                     if($file_dir =~ m%^(.*?)/[^/]+$%) {
92                         $include2info{$file}{includes}{"$1/$header"}++;
93                     } else {
94                         $include2info{$file}{includes}{"$header"}++;
95                     }
96                 } elsif(-e "$wine_dir/include/$header") {
97                     $include2info{$file}{includes}{"include/$header"}++;
98                 } else {
99                     $output->write("$file: #include \"$header\" is not a local include\n");
100                 }
101             }
102         }
103         close(IN);
104     }
105
106     my @files2 = ("acconfig.h", "poppack.h", "pshpack1.h", "pshpack2.h", "pshpack4.h", "pshpack8.h",
107                   "storage.h", "ver.h");
108     foreach my $file2 (@files2) {
109         $include2info{"include/$file2"}{used}++;
110     }    
111 }
112
113 my @c_files = $options->c_files;
114 @c_files = files_skip(@c_files);
115 @c_files = files_filter("winelib", @c_files);
116
117 my @h_files = $options->h_files;
118 @h_files = files_skip(@h_files);
119 @h_files = files_filter("winelib", @h_files);
120
121 my $all_modules = 0;
122 my %complete_module;
123 if($options->global) {
124     my @complete_modules = $modules->complete_modules(\@c_files);
125     
126     foreach my $module (@complete_modules) {
127         $complete_module{$module}++;
128     }
129
130     my $all_modules = 1;
131     foreach my $module ($modules->all_modules) {
132         if(!$complete_module{$module}) {
133             $all_modules = 0;
134             if($wine_dir eq ".") {
135                 $output->write("*.c: module $module is not complete\n");
136             }
137         }
138     }
139 }
140
141 my $progress_current = 0;
142 my $progress_max = scalar(@c_files);
143
144 if($options->headers) {
145     $progress_max += scalar(@h_files);
146
147     foreach my $file (@h_files) {
148         my %functions;
149         
150         $progress_current++;
151         $output->progress("$file: file $progress_current of $progress_max");
152
153         my $create_function = sub {
154             return 'winapi_function'->new;
155         };
156
157         my $found_function = sub {
158             my $function = shift;
159
160             my $internal_name = $function->internal_name;
161
162             $output->progress("$file (file $progress_current of $progress_max): $internal_name");
163             $output->prefix_callback(sub { return $function->prefix; });
164
165             my $function_line = $function->function_line;
166             my $linkage = $function->linkage;
167             my $external_name = $function->external_name;
168             my $statements = $function->statements;
169
170             if($options->headers_misplaced &&
171                !($function->is_win16 && $function->is_win32) &&
172                (($function->is_win16 && $file =~ /^include\/[^\/]*$/) ||
173                 ($function->is_win32 && $file =~ /^include\/wine\/[^\/]*$/)))
174             {
175                 $output->write("declaration misplaced\n");
176             }
177
178             if(defined($external_name) && !defined($statements) &&
179                ($linkage eq "" || $linkage eq "extern")) 
180             {
181                 my $previous_function = $declared_functions{$internal_name};
182                 if(!defined($previous_function)) {
183                     $declared_functions{$internal_name} = $function;
184                 } elsif($options->headers_duplicated) {
185                     my $file = $previous_function->file;
186                     my $function_line = $previous_function->function_line;
187                     if($file =~ /\.h$/) {
188                         $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
189                     }
190                 }
191             }
192             $output->prefix("");
193         };
194
195         my $create_type = sub {
196             return 'type'->new;
197         };
198
199         my $found_type = sub {
200             my $type = shift;
201         };
202
203         my $found_preprocessor = sub {
204             my $directive = shift;
205             my $argument = shift;
206         };
207
208         &winapi_parser::parse_c_file($file, $create_function, $found_function, $create_type, $found_type, $found_preprocessor);
209     }
210 }
211
212 my %module2functions = ();
213 my %type_found = ();
214
215 foreach my $file (@c_files) {
216     my %functions = ();
217     my %includes = ();
218
219     $includes{$file}++;
220
221     my $file_module16 = $modules->allowed_modules_in_file("$current_dir/$file");
222     my $file_module32 = $modules->allowed_modules_in_file("$current_dir/$file");
223
224     $progress_current++;
225     $output->progress("$file (file $progress_current of $progress_max)");
226
227     my $file_dir = $file;
228     if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
229         $file_dir = ".";
230     }
231
232     my $create_function = sub {
233         return 'winapi_function'->new;
234     };
235    
236     my $found_function = sub {
237         my $function = shift;
238
239         my $internal_name = $function->internal_name;
240         $functions{$internal_name} = $function;
241
242         $output->progress("$file (file $progress_current of $progress_max): $internal_name");
243         $output->prefix_callback(sub { return $function->prefix; });
244
245         my $declared_function = $declared_functions{$internal_name};
246
247         my $documentation_line = $function->documentation_line;
248         my $documentation = $function->documentation;
249         my $linkage = $function->linkage;
250         my $return_type = $function->return_type;
251         my $calling_convention = $function->calling_convention;
252         my $statements = $function->statements;
253
254         my $module16 = $function->module16;
255         my $module32 = $function->module32;
256
257         my $external_name = $function->external_name;
258         my $external_name16 = $function->external_name16;
259         my $external_name32 = $function->external_name32;
260
261         if(defined($external_name) && !defined($statements) && 
262            ($linkage eq "" || $linkage eq "extern")) 
263         {
264             my $previous_function = $declared_functions{$internal_name};
265             if(!defined($previous_function)) {
266                 $declared_functions{$internal_name} = $function;
267             } else {
268                 my $file = $previous_function->file;
269                 my $function_line = $previous_function->function_line;
270
271                 my $header = $file;
272                 $header =~ s%^(include|$file_dir)/%%;
273                 if($header !~ m%^msvcrt/% || $file_dir =~ m%^dlls/msvcrt%) {
274                     $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
275                 }
276             }
277         }
278
279         foreach my $module ($function->modules) {
280             $module2functions{$module}{$internal_name} = $function;
281
282             my @types = ($return_type);
283
284             my $refargument_types = $function->argument_types;
285             if(defined($refargument_types)) {
286                 push @types, @$refargument_types;
287             }
288
289             for my $type (@types) {
290                 $type_found{$module}{$type}++;
291             }
292         }
293
294         foreach my $module ($function->modules) {
295             $modules->found_module_in_dir($module, $file_dir);
296         }
297
298         if($options->shared) {
299             if($win16api->is_shared_internal_function($internal_name) || 
300                $win32api->is_shared_internal_function($internal_name)) 
301             {
302                 $output->write("is shared between Win16 and Win32\n");
303             }
304         }
305
306         if($options->headers && $options->headers_needed && 
307            defined($declared_function) && defined($external_name) && 
308            defined($statements))
309         {
310             my $needed_include = $declared_function->file;
311
312             if(!defined($includes{$needed_include})) {
313                 my $header = $needed_include;
314                 $header =~ s%^(include|$file_dir)/%%;
315                 if($header !~ m%^msvcrt/% || $file_dir =~ m%^dlls/msvcrt%) {
316                     $output->write("prototype not included: #include \"$header\" is needed\n");
317                 }
318             }
319         }
320
321         if($options->local && $options->argument && defined($statements)) {
322             &winapi_local::check_function($function);
323         }
324         
325         if($options->local && $options->statements && defined($statements)) {
326             &winapi_local::check_statements(\%functions, $function);
327         }
328         
329         if($options->local && $options->documentation &&
330            (defined($module16) || defined($module32)) &&
331            $linkage eq "" && defined($statements))
332         {
333             &winapi_documentation::check_documentation($function);
334         }
335
336         if(1) {
337             # FIXME: Not correct
338             if(defined($external_name16)) {
339                 $external_name16 = (split(/\s*&\s*/, $external_name16))[0];
340             }
341             
342             # FIXME: Not correct
343             if(defined($external_name32)) {
344                 $external_name32 = (split(/\s*&\s*/, $external_name32))[0];
345             }
346
347             if($options->local && $options->misplaced &&
348                $linkage ne "extern" && defined($statements)) 
349             {
350                 if($options->win16 && $options->report_module($module16))
351                 {
352                     if($file ne "library/port.c" &&
353                        !$nativeapi->is_function($internal_name) &&
354                        !is_subset($module16, $file_module16))
355                     {
356                         foreach my $module16 (split(/\s*&\s*/, $module16)) {
357                             if(!$win16api->is_function_stub($module16, $internal_name)) {
358                                 $output->write("is misplaced ($module16)\n");
359                             }
360                         }
361                     }
362                 }
363
364                 if($options->win32 && $options->report_module($module32))
365                 {
366                     if($file ne "library/port.c" &&
367                        !$nativeapi->is_function($internal_name) &&
368                        !is_subset($module32, $file_module32))
369                     {
370                         foreach my $module32 (split(/\s*&\s*/, $module32)) {
371                             if(!$win32api->is_function_stub($module32, $internal_name)) {
372                                 $output->write("is misplaced ($module32)\n");
373                             }
374                         }
375                     }
376                 }
377             }
378
379             if($options->local && $options->headers && $options->prototype) {
380                 if($options->win16 && $options->report_module($module16)) {
381                     if(!$nativeapi->is_function($internal_name) && 
382                        !defined($declared_functions{$internal_name}))
383                     {
384                         $output->write("no prototype\n");
385                     }
386                 }
387
388                 if($options->win32 && $options->report_module($module32)) {
389                     if(!defined($external_name32) || (!$nativeapi->is_function($external_name32) &&                                                       !defined($declared_functions{$external_name32})))
390                     {
391                         if(!defined($external_name32) || ($external_name32 !~ /^Dll(?:
392                            Install|CanUnloadNow|GetClassObject|GetVersion|
393                            RegisterServer|RegisterServerEx|UnregisterServer)|DriverProc$/x && 
394                            $internal_name !~ /^COMCTL32_Str/ &&
395                            $internal_name !~ /^(?:\Q$module32\E|wine)_(?:\Q$external_name32\E|\d+)$/))
396                         {
397                             $output->write("no prototype\n");
398                         }
399                     }
400                 }
401             }
402         }
403
404         $output->prefix("");
405     };
406
407     my $config = 0;
408     my $conditional = 0;
409     my $found_include = sub {
410         local $_ = shift;
411         if(/^\"(?:config\.h|wine\/port\.h)\"/) {
412             $config++;
413         }
414     };
415     my $found_conditional = sub {
416         local $_ = shift;
417
418         $nativeapi->found_conditional($_);
419
420         if($options->config) {
421             if(!$nativeapi->is_conditional($_)) {
422                 if(/^HAVE_/ && !/^HAVE_(IPX|MESAGL|BUGGY_MESAGL|WINE_CONSTRUCTOR)$/)
423                 {
424                     $output->write("$file: $_ is not declared as a conditional\n");
425                 }
426             } else {
427                 $conditional++;
428                 if(!$config) {
429                     $output->write("$file: conditional $_ used but config.h is not included\n");
430                 }
431             }
432         }
433     };
434
435     my $create_type = sub {
436         return 'type'->new;
437     };
438     
439     my $found_type = sub {
440         my $type = shift;
441     };
442
443     my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
444     my $found_preprocessor = sub {
445         my $directive = shift;
446         my $argument = shift;
447
448         $preprocessor->directive($directive, $argument);
449
450         if($options->config) {
451             if($directive eq "include") {
452                 my $header;
453                 my $check_protection;
454                 my $check_local;
455                 if($argument =~ /^<(.*?)>$/) {
456                     $header = $1;
457                     $check_protection = 1;
458                     $check_local = 0;
459                 } elsif($argument =~ /^\"(.*?)\"$/) {
460                     $header = $1;
461                     $check_protection = 0;
462                     $check_local = 1;
463                 } else {
464                     $output->write("$file: #$directive $argument: is unparsable\n");
465
466                     $header = undef;
467                     $check_protection = 0;
468                     $check_local = 0;
469                 }
470
471                 if(defined($header)) {
472                     my $include;
473                     if(-e "$wine_dir/include/$header") {
474                         $include = "include/$header";
475                     } elsif(-e "$file_dir/$header") {
476                         $include = "$file_dir/$header";
477                     } elsif(-e "$file_dir/../$header") {
478                         if($file_dir =~ m%^(.*?)/[^/]+$%) {
479                             $include = "$1/$header";
480                         } else {
481                             $include = "$header";
482                         }
483                     } elsif($header eq "controls.h") { # FIXME: Kludge
484                         $include = "dlls/user/controls.h";
485                     } elsif($check_local) {
486                         $output->write("$file: #include \"$header\": file not found\n");
487                     }
488
489                     if(defined($include)) {
490                         $includes{$include}++;
491                         foreach my $include (keys(%{$include2info{$include}{includes}})) {
492                             $includes{$include}++;
493                         }
494                     }
495                 }
496
497                 if($check_protection && $header) {
498                     if((-e "$wine_dir/include/$header" || -e "$wine_dir/$file_dir/$header")) {
499                         if($header !~ /^ctype.h$/) {
500                             $output->write("$file: #include \<$header\> is a local include\n");
501                         }
502                     }
503
504                     my $macro = uc($header);
505                     $macro =~ y/\.\//__/;
506                     $macro = "HAVE_" . $macro;
507                     
508                     if($nativeapi->is_conditional_header($header)) { 
509                         if(!$preprocessor->is_def($macro)) {
510                             if($macro =~ /^HAVE_X11/) {
511                                 # Do nothing X Windows is handled differently
512                             } elsif($macro =~ /^HAVE_(.*?)_H$/) {
513                                 my $name = $1;
514                                 if($header !~ /^alloca\.h|sys\/time\.h|unistd\.h$/ && 
515                                    !$preprocessor->is_def("STATFS_DEFINED_BY_$name")) 
516                                 {
517                                     $output->write("$file: #$directive $argument: is a conditional include, " . 
518                                                    "but is not protected\n");
519                                 }
520                             }
521                         }
522                     } elsif($preprocessor->is_def($macro)) {
523                         $output->write("$file: #$directive $argument: is protected, " .
524                                        "but is not a conditional include\n");
525                     }
526                 }
527
528                 if($check_local && $header) {
529                     if(-e "$file_dir/$header") {
530                         if($file_dir ne ".") {
531                             $include2info{"$file_dir/$header"}{used}++;
532                             foreach my $name (keys(%{$include2info{"$file_dir/$header"}{includes}})) {
533                                 $include2info{$name}{used}++;
534                             }
535                         } else {
536                             $include2info{"$header"}{used}++;
537                             foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
538                                 $include2info{$name}{used}++;
539                             }
540                         }
541                     } elsif(-e "$wine_dir/$file_dir/../$header") {
542                         if($file_dir =~ m%^(.*?)/[^/]+$%) {
543                             $include2info{"$1/$header"}{used}++;
544                             foreach my $name (keys(%{$include2info{"$1/$header"}{includes}})) {
545                                 $include2info{$name}{used}++;
546                             }
547                         } else {
548                             $include2info{"$header"}{used}++;
549                             foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
550                                 $include2info{$name}{used}++;
551                             }
552                         }
553                     } elsif($header eq "controls.h") { # FIXME: Kludge
554                         $include2info{"dlls/user/$header"}{used}++;
555                         foreach my $name (keys(%{$include2info{"dlls/user/$header"}{includes}})) {
556                             $include2info{$name}{used}++;
557                         }
558                     } elsif(-e "$wine_dir/include/$header") {
559                         $include2info{"include/$header"}{used}++;
560                         foreach my $name (keys(%{$include2info{"include/$header"}{includes}})) {
561                             $include2info{$name}{used}++;
562                         }
563                     } else {
564                         $output->write("$file: #include \"$header\" is not a local include\n");
565                     }
566                 }
567             }
568         }
569     };
570
571     &winapi_parser::parse_c_file($file, $create_function, $found_function, $create_type, $found_type, $found_preprocessor);
572     
573     if($options->config_unnessary) {
574         if($config && $conditional == 0) {
575             $output->write("$file: include2info config.h but do not use any conditionals\n");
576         }
577     }
578
579     &winapi_local::check_file($file, \%functions);
580 }
581
582 if($options->global) {
583     my @complete_modules = sort(keys(%complete_module));
584
585     if($options->declared) {
586         foreach my $module (@complete_modules) {
587             foreach my $winapi (@winapis) {
588                 if(!$winapi->is_module($module)) { next; }
589                 my $functions = $module2functions{$module};
590                 foreach my $internal_name ($winapi->all_internal_functions_in_module($module)) {
591                     my $function = $functions->{$internal_name};
592                     if(!defined($function) && !$nativeapi->is_function($internal_name) &&
593                        !($module eq "user" && $internal_name =~
594                          /^(?:GlobalAddAtomA|GlobalDeleteAtom|GlobalFindAtomA|
595                             GlobalGetAtomNameA|lstrcmpiA)$/x))
596                     {
597                         $output->write("*.c: $module: $internal_name: " .
598                                        "function declared but not implemented or declared external\n");
599                     }
600                 }
601             }
602         }
603     }
604
605     if($options->argument && $options->argument_forbidden) {
606         foreach my $winapi (@winapis) {
607             my $types_not_used = $winapi->types_not_used;
608             foreach my $module (sort(keys(%$types_not_used))) {
609                 if(!$complete_module{$module}) { next; }
610                 foreach my $type (sort(keys(%{$$types_not_used{$module}}))) {
611                     $output->write("*.c: $module: type ($type) not used\n");
612                 }
613             }
614         }
615     }
616
617     if($all_modules) {
618         &winapi_documentation::report_documentation;
619         
620         if($options->headers_unused) {
621             foreach my $name (sort(keys(%include2info))) {
622                 if(!$include2info{$name}{used}) {
623                     if($options->include) {
624                         $output->write("*.c: $name: include file is never used\n");
625                     }
626                 }
627             }
628         }
629         
630         &winapi_global::check(\%type_found);
631
632         $modules->global_report;
633         $nativeapi->global_report;
634     }
635 }
636