3 # Copyright 1999-2001 Patrik Stridvall
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.
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.
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
20 # Note that winapi_check are using heuristics quite heavily.
21 # So always remember that:
23 # "Heuristics are bug ridden by definition.
24 # If they didn't have bugs, then they'd be algorithms."
26 # In other words, reported bugs are only potential bugs not
27 # real bugs, so they are called issues rather than bugs.
33 $0 =~ m%^(.*?/?tools)/winapi_check/winapi_check$%;
34 require "$1/winapi/setup.pm";
38 &files_filter &files_skip
40 $current_dir $wine_dir
42 use output qw($output);
43 use winapi_check_options qw($options);
45 if($options->progress) {
46 $output->enable_progress;
48 $output->disable_progress;
51 use modules qw($modules);
52 use nativeapi qw($nativeapi);
53 use winapi qw($win16api $win32api @winapis);
57 use util qw(&is_subset);
58 use winapi_documentation;
64 my %declared_functions;
68 my @files = get_h_files("winelib");
70 my $progress_current = 0;
71 my $progress_max = scalar(@files);
73 foreach my $file (@files) {
75 $output->lazy_progress("$file: file $progress_current of $progress_max");
78 if(!($file_dir =~ s%(.*?)/[^/]+$%$1%)) {
82 $include2info{$file} = { name => $file };
84 open(IN, "< $wine_dir/$file");
86 if(/^\s*\#\s*include\s*\"(.*?)\"/) {
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"}++;
94 $include2info{$file}{includes}{"$header"}++;
96 } elsif(-e "$wine_dir/include/$header") {
97 $include2info{$file}{includes}{"include/$header"}++;
99 $output->write("$file: #include \"$header\" is not a local include\n");
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}++;
113 my @c_files = $options->c_files;
114 @c_files = files_skip(@c_files);
115 @c_files = files_filter("winelib", @c_files);
117 my @h_files = $options->h_files;
118 @h_files = files_skip(@h_files);
119 @h_files = files_filter("winelib", @h_files);
123 if($options->global) {
124 my @complete_modules = $modules->complete_modules(\@c_files);
126 foreach my $module (@complete_modules) {
127 $complete_module{$module}++;
131 foreach my $module ($modules->all_modules) {
132 if(!$complete_module{$module}) {
134 if($wine_dir eq ".") {
135 $output->write("*.c: module $module is not complete\n");
141 my $progress_current = 0;
142 my $progress_max = scalar(@c_files);
144 if($options->headers) {
145 $progress_max += scalar(@h_files);
147 foreach my $file (@h_files) {
151 $output->progress("$file: file $progress_current of $progress_max");
153 my $create_function = sub {
154 return 'winapi_function'->new;
157 my $found_function = sub {
158 my $function = shift;
160 my $internal_name = $function->internal_name;
162 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
163 $output->prefix_callback(sub { return $function->prefix; });
165 my $function_line = $function->function_line;
166 my $linkage = $function->linkage;
167 my $external_name = $function->external_name;
168 my $statements = $function->statements;
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\/[^\/]*$/)))
175 $output->write("declaration misplaced\n");
178 if(defined($external_name) && !defined($statements) &&
179 ($linkage eq "" || $linkage eq "extern"))
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");
195 my $create_type = sub {
199 my $found_type = sub {
203 my $found_preprocessor = sub {
204 my $directive = shift;
205 my $argument = shift;
208 &winapi_parser::parse_c_file($file, $create_function, $found_function, $create_type, $found_type, $found_preprocessor);
212 my %module2functions = ();
215 foreach my $file (@c_files) {
221 my $file_module16 = $modules->allowed_modules_in_file("$current_dir/$file");
222 my $file_module32 = $modules->allowed_modules_in_file("$current_dir/$file");
225 $output->progress("$file (file $progress_current of $progress_max)");
227 my $file_dir = $file;
228 if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
232 my $create_function = sub {
233 return 'winapi_function'->new;
236 my $found_function = sub {
237 my $function = shift;
239 my $internal_name = $function->internal_name;
240 $functions{$internal_name} = $function;
242 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
243 $output->prefix_callback(sub { return $function->prefix; });
245 my $declared_function = $declared_functions{$internal_name};
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;
254 my $module16 = $function->module16;
255 my $module32 = $function->module32;
257 my $external_name = $function->external_name;
258 my $external_name16 = $function->external_name16;
259 my $external_name32 = $function->external_name32;
261 if(defined($external_name) && !defined($statements) &&
262 ($linkage eq "" || $linkage eq "extern"))
264 my $previous_function = $declared_functions{$internal_name};
265 if(!defined($previous_function)) {
266 $declared_functions{$internal_name} = $function;
268 my $file = $previous_function->file;
269 my $function_line = $previous_function->function_line;
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");
279 foreach my $module ($function->modules) {
280 $module2functions{$module}{$internal_name} = $function;
282 my @types = ($return_type);
284 my $refargument_types = $function->argument_types;
285 if(defined($refargument_types)) {
286 push @types, @$refargument_types;
289 for my $type (@types) {
290 $type_found{$module}{$type}++;
294 foreach my $module ($function->modules) {
295 $modules->found_module_in_dir($module, $file_dir);
298 if($options->shared) {
299 if($win16api->is_shared_internal_function($internal_name) ||
300 $win32api->is_shared_internal_function($internal_name))
302 $output->write("is shared between Win16 and Win32\n");
306 if($options->headers && $options->headers_needed &&
307 defined($declared_function) && defined($external_name) &&
308 defined($statements))
310 my $needed_include = $declared_function->file;
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");
321 if($options->local && $options->argument && defined($statements)) {
322 &winapi_local::check_function($function);
325 if($options->local && $options->statements && defined($statements)) {
326 &winapi_local::check_statements(\%functions, $function);
329 if($options->local && $options->documentation &&
330 (defined($module16) || defined($module32)) &&
331 $linkage eq "" && defined($statements))
333 &winapi_documentation::check_documentation($function);
338 if(defined($external_name16)) {
339 $external_name16 = (split(/\s*&\s*/, $external_name16))[0];
343 if(defined($external_name32)) {
344 $external_name32 = (split(/\s*&\s*/, $external_name32))[0];
347 if($options->local && $options->misplaced &&
348 $linkage ne "extern" && defined($statements))
350 if($options->win16 && $options->report_module($module16))
352 if($file ne "library/port.c" &&
353 !$nativeapi->is_function($internal_name) &&
354 !is_subset($module16, $file_module16))
356 foreach my $module16 (split(/\s*&\s*/, $module16)) {
357 if(!$win16api->is_function_stub($module16, $internal_name)) {
358 $output->write("is misplaced ($module16)\n");
364 if($options->win32 && $options->report_module($module32))
366 if($file ne "library/port.c" &&
367 !$nativeapi->is_function($internal_name) &&
368 !is_subset($module32, $file_module32))
370 foreach my $module32 (split(/\s*&\s*/, $module32)) {
371 if(!$win32api->is_function_stub($module32, $internal_name)) {
372 $output->write("is misplaced ($module32)\n");
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}))
384 $output->write("no prototype\n");
388 if($options->win32 && $options->report_module($module32)) {
389 if(!defined($external_name32) || (!$nativeapi->is_function($external_name32) && !defined($declared_functions{$external_name32})))
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+)$/))
397 $output->write("no prototype\n");
409 my $found_include = sub {
411 if(/^\"(?:config\.h|wine\/port\.h)\"/) {
415 my $found_conditional = sub {
418 $nativeapi->found_conditional($_);
420 if($options->config) {
421 if(!$nativeapi->is_conditional($_)) {
422 if(/^HAVE_/ && !/^HAVE_(IPX|MESAGL|BUGGY_MESAGL|WINE_CONSTRUCTOR)$/)
424 $output->write("$file: $_ is not declared as a conditional\n");
429 $output->write("$file: conditional $_ used but config.h is not included\n");
435 my $create_type = sub {
439 my $found_type = sub {
443 my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
444 my $found_preprocessor = sub {
445 my $directive = shift;
446 my $argument = shift;
448 $preprocessor->directive($directive, $argument);
450 if($options->config) {
451 if($directive eq "include") {
453 my $check_protection;
455 if($argument =~ /^<(.*?)>$/) {
457 $check_protection = 1;
459 } elsif($argument =~ /^\"(.*?)\"$/) {
461 $check_protection = 0;
464 $output->write("$file: #$directive $argument: is unparsable\n");
467 $check_protection = 0;
471 if(defined($header)) {
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";
481 $include = "$header";
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");
489 if(defined($include)) {
490 $includes{$include}++;
491 foreach my $include (keys(%{$include2info{$include}{includes}})) {
492 $includes{$include}++;
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");
504 my $macro = uc($header);
505 $macro =~ y/\.\//__/;
506 $macro = "HAVE_" . $macro;
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$/) {
514 if($header !~ /^alloca\.h|sys\/time\.h|unistd\.h$/ &&
515 !$preprocessor->is_def("STATFS_DEFINED_BY_$name"))
517 $output->write("$file: #$directive $argument: is a conditional include, " .
518 "but is not protected\n");
522 } elsif($preprocessor->is_def($macro)) {
523 $output->write("$file: #$directive $argument: is protected, " .
524 "but is not a conditional include\n");
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}++;
536 $include2info{"$header"}{used}++;
537 foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
538 $include2info{$name}{used}++;
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}++;
548 $include2info{"$header"}{used}++;
549 foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
550 $include2info{$name}{used}++;
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}++;
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}++;
564 $output->write("$file: #include \"$header\" is not a local include\n");
571 &winapi_parser::parse_c_file($file, $create_function, $found_function, $create_type, $found_type, $found_preprocessor);
573 if($options->config_unnessary) {
574 if($config && $conditional == 0) {
575 $output->write("$file: include2info config.h but do not use any conditionals\n");
579 &winapi_local::check_file($file, \%functions);
582 if($options->global) {
583 my @complete_modules = sort(keys(%complete_module));
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))
597 $output->write("*.c: $module: $internal_name: " .
598 "function declared but not implemented or declared external\n");
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");
618 &winapi_documentation::report_documentation;
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");
630 &winapi_global::check(\%type_found);
632 $modules->global_report;
633 $nativeapi->global_report;