3 # Copyright 1999-2002 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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.
34 $0 =~ m
%^(.*?
/?tools)/winapi
/winapi_check
$%;
35 require "$1/winapi/setup.pm";
39 files_filter files_skip
41 $current_dir $wine_dir
43 use output qw($output);
44 use winapi_check_options qw($options);
47 if($options->progress) {
48 $output->enable_progress;
50 $output->disable_progress;
54 use modules qw($modules);
55 use nativeapi qw($nativeapi);
56 use winapi qw($win16api $win32api @winapis);
60 use util qw(is_subset);
61 use winapi_documentation
;
69 if ($options->global) {
70 my @files = get_h_files
("winelib");
72 my $progress_current = 0;
73 my $progress_max = scalar(@files);
75 foreach my $file (@files) {
77 $output->lazy_progress("$file: file $progress_current of $progress_max");
80 if(!($file_dir =~ s
%(.*?
)/[^/]+$%$1%)) {
84 $include2info{$file} = { name
=> $file };
86 open(IN
, "< $wine_dir/$file") || die "Error: Can't open $wine_dir/$file: $!\n";
88 if(/^\s*\#\s*include\s*\"(.*?)\"/) {
90 if(-e
"$wine_dir/$file_dir/$header") {
91 $include2info{$file}{includes
}{"$file_dir/$header"}++;
92 } elsif(-e
"$wine_dir/$file_dir/../$header") {
93 if($file_dir =~ m
%^(.*?
)/[^/]+$%) {
94 $include2info{$file}{includes
}{"$1/$header"}++;
96 $include2info{$file}{includes
}{"$header"}++;
98 } elsif(-e
"$wine_dir/include/$header") {
99 $include2info{$file}{includes
}{"include/$header"}++;
100 } elsif ($header ne "config.h") {
101 $output->write("$file: #include \"$header\" is not a local include\n");
108 my @files2 = ("acconfig.h", "poppack.h", "pshpack1.h", "pshpack2.h", "pshpack4.h", "pshpack8.h",
109 "storage.h", "ver.h");
110 foreach my $file2 (@files2) {
111 $include2info{"include/$file2"}{used
}++;
115 my @c_files = $options->c_files;
116 @c_files = files_skip
(@c_files);
117 @c_files = files_filter
("winelib", @c_files);
119 my @h_files = $options->h_files;
120 @h_files = files_skip
(@h_files);
121 @h_files = files_filter
("winelib", @h_files);
125 if($options->global) {
126 my @complete_modules = $modules->complete_modules(\
@c_files);
128 foreach my $module (@complete_modules) {
129 $complete_module{$module}++;
133 foreach my $module ($modules->all_modules) {
134 if(!$complete_module{$module}) {
136 if($wine_dir eq ".") {
137 $output->write("*.c: module $module is not complete\n");
144 foreach my $winapi (@winapis) {
145 foreach my $broken_forward ($winapi->all_broken_forwards) {
146 (my $module, my $external_name, my $forward_module, my $forward_external_name) = @
$broken_forward;
147 if($complete_module{$forward_module}) {
148 $output->write("$module.spec: forward is broken: $external_name => $forward_module.$forward_external_name\n");
154 my $progress_current = 0;
155 my $progress_max = scalar(@c_files);
157 my %declared_functions;
159 if($options->headers) {
160 $progress_max += scalar(@h_files);
162 foreach my $file (@h_files) {
166 $output->progress("$file: file $progress_current of $progress_max");
168 my $found_c_comment = sub {
169 my $begin_line = shift;
170 my $end_line = shift;
174 if($begin_line == $end_line) {
175 $output->write("$file:$begin_line: $comment\n");
177 $output->write("$file:$begin_line-$end_line: \\\n$comment\n");
182 my $found_cplusplus_comment = sub {
186 if($options->comments_cplusplus) {
187 $output->write("$file:$line: C++ comments not allowed: $comment\n");
191 my $create_function = sub {
192 return 'winapi_function'->new;
195 my $found_function = sub {
196 my $function = shift;
198 my $internal_name = $function->internal_name;
200 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
201 $output->prefix_callback(sub { return $function->prefix; });
203 my $function_line = $function->function_line;
204 my $linkage = $function->linkage;
205 my $external_name = $function->external_name;
206 my $statements = $function->statements;
208 if($options->headers_misplaced &&
209 !($function->is_win16 && $function->is_win32) &&
210 (($function->is_win16 && $file =~ /^include\/[^\
/]*$/) ||
211 ($function->is_win32 && $file =~ /^include\/wine\
/[^\/]*$/)))
213 $output->write("declaration misplaced\n");
216 if(defined($external_name) && !defined($statements) &&
217 ($linkage eq "" || $linkage eq "extern"))
219 my $previous_function = $declared_functions{$internal_name};
220 if(!defined($previous_function)) {
221 $declared_functions{$internal_name} = $function;
222 } elsif($options->headers_duplicated) {
223 my $file = $previous_function->file;
224 my $function_line = $previous_function->function_line;
225 if($file =~ /\.h$/) {
226 $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
233 my $create_type = sub {
237 my $found_type = sub {
241 my $found_preprocessor = sub {
242 my $directive = shift;
243 my $argument = shift;
246 winapi_parser
::parse_c_file
($file, {
247 c_comment_found
=> $found_c_comment,
248 cplusplus_comment_found
=> $found_cplusplus_comment,
249 function_create
=> $create_function,
250 function_found
=> $found_function,
251 type_create
=> $create_type,
252 type_found
=> $found_type,
253 preprocessor_found
=> $found_preprocessor
258 my %module2functions = ();
260 foreach my $file (@c_files) {
266 my $file_module16 = $modules->allowed_modules_in_file("$current_dir/$file");
267 my $file_module32 = $modules->allowed_modules_in_file("$current_dir/$file");
270 $output->progress("$file (file $progress_current of $progress_max)");
272 my $file_dir = $file;
273 if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
277 my $found_c_comment = sub {
278 my $begin_line = shift;
279 my $end_line = shift;
283 if($begin_line == $end_line) {
284 $output->write("$file:$begin_line: $comment\n");
286 $output->write("$file:$begin_line-$end_line: \\\n$comment\n");
291 my $found_cplusplus_comment = sub {
295 if($options->comments_cplusplus) {
296 $output->write("$file:$line: C++ comments not allowed: $comment\n");
300 my $create_function = sub {
301 return 'winapi_function'->new;
304 my $found_function = sub {
305 my $function = shift;
307 my $internal_name = $function->internal_name;
308 $functions{$internal_name} = $function;
310 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
311 $output->prefix_callback(sub { return $function->prefix; });
313 my $declared_function = $declared_functions{$internal_name};
315 my $documentation_line = $function->documentation_line;
316 my $documentation = $function->documentation;
317 my $linkage = $function->linkage;
318 my $return_type = $function->return_type;
319 my $calling_convention = $function->calling_convention;
320 my $statements = $function->statements;
322 my $module16 = $function->module16;
323 my $module32 = $function->module32;
325 my $external_name = $function->external_name;
326 my $external_name16 = $function->external_name16;
327 my $external_name32 = $function->external_name32;
329 if(defined($external_name) && !defined($statements) &&
330 ($linkage eq "" || $linkage eq "extern"))
332 my $previous_function = $declared_functions{$internal_name};
333 if(!defined($previous_function)) {
334 $declared_functions{$internal_name} = $function;
336 my $file = $previous_function->file;
337 my $function_line = $previous_function->function_line;
340 $header =~ s
%^(include
|$file_dir)/%%;
341 if($header !~ m
%^msvcrt
/% || $file_dir =~ m%^dlls/msvcrt
%) {
342 $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
347 if ($options->global) {
348 foreach my $module ($function->modules) {
349 $module2functions{$module}{$internal_name} = $function;
353 foreach my $module ($function->modules) {
354 $modules->found_module_in_dir($module, $file_dir);
357 if($options->shared) {
358 if($win16api->is_shared_internal_function($internal_name) ||
359 $win32api->is_shared_internal_function($internal_name))
361 $output->write("is shared between Win16 and Win32\n");
365 if($options->headers && $options->headers_needed &&
366 defined($declared_function) && defined($external_name) &&
367 defined($statements))
369 my $needed_include = $declared_function->file;
371 if(!defined($includes{$needed_include})) {
372 my $header = $needed_include;
373 $header =~ s
%^(include
|$file_dir)/%%;
374 if($header !~ m
%^msvcrt
/% || $file_dir =~ m%^dlls/msvcrt
%) {
375 $output->write("prototype not included: #include \"$header\" is needed\n");
380 if($options->local && $options->argument && defined($statements)) {
381 winapi_local
::check_function
($function);
384 if($options->local && $options->statements && defined($statements)) {
385 winapi_local
::check_statements
(\
%functions, $function);
388 if($options->local && $options->documentation &&
389 (defined($module16) || defined($module32)) &&
390 $linkage eq "" && defined($statements))
392 winapi_documentation
::check_documentation
($function);
397 if(defined($external_name16)) {
398 $external_name16 = (split(/\s*&\s*/, $external_name16))[0];
402 if(defined($external_name32)) {
403 $external_name32 = (split(/\s*&\s*/, $external_name32))[0];
406 if($options->local && $options->misplaced &&
407 $linkage ne "extern" && defined($statements))
409 if($options->win16 && $options->report_module($module16))
411 if($file ne "library/port.c" &&
412 !$nativeapi->is_function($internal_name) &&
413 !is_subset
($module16, $file_module16))
415 foreach my $module16 (split(/\s*&\s*/, $module16)) {
416 if(!$win16api->is_function_stub($module16, $internal_name)) {
417 $output->write("is misplaced ($module16)\n");
423 if($options->win32 && $options->report_module($module32))
425 if($file ne "library/port.c" &&
426 !$nativeapi->is_function($internal_name) &&
427 !is_subset
($module32, $file_module32))
429 foreach my $module32 (split(/\s*&\s*/, $module32)) {
430 if(!$win32api->is_function_stub($module32, $internal_name)) {
431 $output->write("is misplaced ($module32)\n");
438 if($options->local && $options->headers && $options->prototype) {
439 if($options->win16 && $options->report_module($module16)) {
440 if(!$nativeapi->is_function($internal_name) &&
441 !defined($declared_functions{$internal_name}))
443 $output->write("no prototype\n");
447 if($options->win32 && $options->report_module($module32)) {
448 if(!defined($external_name32) || (!$nativeapi->is_function($external_name32) && !defined($declared_functions{$external_name32})))
450 if(!defined($external_name32) || ($external_name32 !~ /^Dll
(?
:
451 Install
|CanUnloadNow
|GetClassObject
|GetVersion
|
452 RegisterServer
|RegisterServerEx
|UnregisterServer
)|DriverProc
$/x
&&
453 $internal_name !~ /^COMCTL32_Str/ &&
454 $internal_name !~ /^(?:\Q$module32\E|wine)_(?:\Q$external_name32\E|\d+)$/))
456 $output->write("no prototype\n");
468 my $found_include = sub {
470 if(/^\"(?:config\.h|wine\/port\
.h
)\"/) {
474 my $found_conditional = sub {
477 $nativeapi->found_conditional($_);
479 if($options->config) {
480 if(!$nativeapi->is_conditional($_)) {
481 if(/^HAVE_/ && !/^HAVE_(?:IPX|CORRECT_LINUXINPUT_H|OSS|OSS_MIDI|V4L2)$/)
483 $output->write("$file: $_ is not declared as a conditional\n");
488 $output->write("$file: conditional $_ used but config.h is not included\n");
494 my $create_type = sub {
498 my $found_type = sub {
502 sub recursive_include
{
504 my $includes = shift;
506 if(!defined($includes->{$include})) {
507 $includes->{$include}++;
508 foreach my $include (keys(%{$include2info{$include}{includes
}})) {
509 recursive_include
($include, \
%$includes);
514 my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
515 my $found_preprocessor = sub {
516 my $directive = shift;
517 my $argument = shift;
519 $preprocessor->directive($directive, $argument);
521 if($options->config) {
522 if($directive eq "include") {
524 my $check_protection;
526 if($argument =~ /^<(.*?)>$/) {
528 $check_protection = 1;
530 } elsif($argument =~ /^\"(.*?)\"$/) {
532 $check_protection = 0;
535 $output->write("$file: #$directive $argument: is unparsable\n");
538 $check_protection = 0;
542 if(defined($header)) {
544 if(-e
"$wine_dir/include/$header") {
545 $include = "include/$header";
546 } elsif(-e
"$wine_dir/include/msvcrt/$header") {
547 $include = "include/msvcrt/$header";
548 } elsif(-e
"$file_dir/$header") {
549 $include = "$file_dir/$header";
550 } elsif(-e
"$file_dir/../$header") {
551 if($file_dir =~ m
%^(.*?
)/[^/]+$%) {
552 $include = "$1/$header";
554 $include = "$header";
556 } elsif($check_local && $header ne "config.h") {
557 $output->write("$file: #include \"$header\": file not found\n");
560 if(defined($include)) {
561 recursive_include
($include, \
%includes);
565 if($check_protection && $header) {
566 if((-e
"$wine_dir/include/$header" || -e
"$wine_dir/$file_dir/$header")) {
567 if($header !~ /^(?:oleauto\.h|win(?:base|def|error|gdi|nls|nt|user)\.h)$/ &&
568 $file_dir !~ /tests$/)
570 $output->write("$file: #include \<$header\> is a local include\n");
574 my $macro = uc($header);
575 $macro =~ y/\.\//__
/;
576 $macro = "HAVE_" . $macro;
578 if($nativeapi->is_conditional_header($header)) {
579 if(!$preprocessor->is_def($macro)) {
580 if($macro =~ /^HAVE_X11/) {
581 # Do nothing X Windows is handled differently
582 } elsif($macro =~ /^HAVE_(.*?)_H$/) {
584 if($header !~ /^alloca\.h$/ &&
585 $file_dir !~ /tests$/)
587 $output->write("$file: #$directive $argument: is a conditional include, " .
588 "but is not protected\n");
592 } elsif($preprocessor->is_def($macro)) {
593 $output->write("$file: #$directive $argument: is protected, but there is no check for it in configure.ac\n");
597 if($check_local && $header) {
598 if(-e
"$file_dir/$header") {
599 if($file_dir ne ".") {
600 $include2info{"$file_dir/$header"}{used
}++;
601 foreach my $name (keys(%{$include2info{"$file_dir/$header"}{includes
}})) {
602 $include2info{$name}{used
}++;
605 $include2info{"$header"}{used
}++;
606 foreach my $name (keys(%{$include2info{"$header"}{includes
}})) {
607 $include2info{$name}{used
}++;
610 } elsif(-e
"$file_dir/../$header") {
611 if($file_dir =~ m
%^(.*?
)/[^/]+$%) {
612 $include2info{"$1/$header"}{used
}++;
613 foreach my $name (keys(%{$include2info{"$1/$header"}{includes
}})) {
614 $include2info{$name}{used
}++;
617 $include2info{"$header"}{used
}++;
618 foreach my $name (keys(%{$include2info{"$header"}{includes
}})) {
619 $include2info{$name}{used
}++;
622 } elsif(-e
"$wine_dir/include/$header") {
623 $include2info{"include/$header"}{used
}++;
624 foreach my $name (keys(%{$include2info{"include/$header"}{includes
}})) {
625 $include2info{$name}{used
}++;
627 } elsif(-e
"$wine_dir/include/msvcrt/$header") {
628 $include2info{"include/msvcrt/$header"}{used
}++;
629 foreach my $name (keys(%{$include2info{"include/msvcrt/$header"}{includes
}})) {
630 $include2info{$name}{used
}++;
632 } elsif ($header ne "config.h") {
633 $output->write("$file: #include \"$header\" is not a local include\n");
640 winapi_parser
::parse_c_file
($file, {
641 c_comment_found
=> $found_c_comment,
642 cplusplus_comment_found
=> $found_cplusplus_comment,
643 function_create
=> $create_function,
644 function_found
=> $found_function,
645 type_create
=> $create_type,
646 type_found
=> $found_type,
647 preprocessor_found
=> $found_preprocessor
650 if($options->config_unnecessary) {
651 if($config && $conditional == 0 && !exists($include2info{"include/wine/port.h"})) {
652 $output->write("$file: include2info config.h but do not use any conditionals\n");
656 winapi_local
::check_file
($file, \
%functions);
659 if($options->global) {
660 winapi_global
::check_modules
(\
%complete_module, \
%module2functions);
663 winapi_global
::check_all_modules
(\
%include2info);