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., 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;
66 if ($options->global) {
67 my @files = get_h_files("winelib");
69 my $progress_current = 0;
70 my $progress_max = scalar(@files);
72 foreach my $file (@files) {
74 $output->lazy_progress("$file: file $progress_current of $progress_max");
77 if(!($file_dir =~ s%(.*?)/[^/]+$%$1%)) {
81 $include2info{$file} = { name => $file };
83 open(IN, "< $wine_dir/$file");
85 if(/^\s*\#\s*include\s*\"(.*?)\"/) {
87 if(-e "$wine_dir/$file_dir/$header") {
88 $include2info{$file}{includes}{"$file_dir/$header"}++;
89 } elsif(-e "$wine_dir/$file_dir/../$header") {
90 if($file_dir =~ m%^(.*?)/[^/]+$%) {
91 $include2info{$file}{includes}{"$1/$header"}++;
93 $include2info{$file}{includes}{"$header"}++;
95 } elsif(-e "$wine_dir/include/$header") {
96 $include2info{$file}{includes}{"include/$header"}++;
98 $output->write("$file: #include \"$header\" is not a local include\n");
105 my @files2 = ("acconfig.h", "poppack.h", "pshpack1.h", "pshpack2.h", "pshpack4.h", "pshpack8.h",
106 "storage.h", "ver.h");
107 foreach my $file2 (@files2) {
108 $include2info{"include/$file2"}{used}++;
112 my @c_files = $options->c_files;
113 @c_files = files_skip(@c_files);
114 @c_files = files_filter("winelib", @c_files);
116 my @h_files = $options->h_files;
117 @h_files = files_skip(@h_files);
118 @h_files = files_filter("winelib", @h_files);
122 if($options->global) {
123 my @complete_modules = $modules->complete_modules(\@c_files);
125 foreach my $module (@complete_modules) {
126 $complete_module{$module}++;
130 foreach my $module ($modules->all_modules) {
131 if(!$complete_module{$module}) {
133 if($wine_dir eq ".") {
134 $output->write("*.c: module $module is not complete\n");
141 foreach my $winapi (@winapis) {
142 foreach my $broken_forward ($winapi->all_broken_forwards) {
143 (my $module, my $external_name, my $forward_module, my $forward_external_name) = @$broken_forward;
144 if($complete_module{$forward_module}) {
145 $output->write("$module.spec: forward is broken: $external_name => $forward_module.$forward_external_name\n");
151 my $progress_current = 0;
152 my $progress_max = scalar(@c_files);
154 my %declared_functions;
156 if($options->headers) {
157 $progress_max += scalar(@h_files);
159 foreach my $file (@h_files) {
163 $output->progress("$file: file $progress_current of $progress_max");
165 my $found_c_comment = sub {
166 my $begin_line = shift;
167 my $end_line = shift;
171 if($begin_line == $end_line) {
172 $output->write("$file:$begin_line: $comment\n");
174 $output->write("$file:$begin_line-$end_line: \\\n$comment\n");
179 my $found_cplusplus_comment = sub {
183 if($options->comments_cplusplus) {
184 $output->write("$file:$line: C++ comments not allowed: $comment\n");
188 my $create_function = sub {
189 return 'winapi_function'->new;
192 my $found_function = sub {
193 my $function = shift;
195 my $internal_name = $function->internal_name;
197 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
198 $output->prefix_callback(sub { return $function->prefix; });
200 my $function_line = $function->function_line;
201 my $linkage = $function->linkage;
202 my $external_name = $function->external_name;
203 my $statements = $function->statements;
205 if($options->headers_misplaced &&
206 !($function->is_win16 && $function->is_win32) &&
207 (($function->is_win16 && $file =~ /^include\/[^\/]*$/) ||
208 ($function->is_win32 && $file =~ /^include\/wine\/[^\/]*$/)))
210 $output->write("declaration misplaced\n");
213 if(defined($external_name) && !defined($statements) &&
214 ($linkage eq "" || $linkage eq "extern"))
216 my $previous_function = $declared_functions{$internal_name};
217 if(!defined($previous_function)) {
218 $declared_functions{$internal_name} = $function;
219 } elsif($options->headers_duplicated) {
220 my $file = $previous_function->file;
221 my $function_line = $previous_function->function_line;
222 if($file =~ /\.h$/) {
223 $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
230 my $create_type = sub {
234 my $found_type = sub {
238 my $found_preprocessor = sub {
239 my $directive = shift;
240 my $argument = shift;
243 &winapi_parser::parse_c_file($file, {
244 c_comment_found => $found_c_comment,
245 cplusplus_comment_found => $found_cplusplus_comment,
246 function_create => $create_function,
247 function_found => $found_function,
248 type_create => $create_type,
249 type_found => $found_type,
250 preprocessor_found => $found_preprocessor
255 my %module2functions = ();
257 foreach my $file (@c_files) {
263 my $file_module16 = $modules->allowed_modules_in_file("$current_dir/$file");
264 my $file_module32 = $modules->allowed_modules_in_file("$current_dir/$file");
267 $output->progress("$file (file $progress_current of $progress_max)");
269 my $file_dir = $file;
270 if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
274 my $found_c_comment = sub {
275 my $begin_line = shift;
276 my $end_line = shift;
280 if($begin_line == $end_line) {
281 $output->write("$file:$begin_line: $comment\n");
283 $output->write("$file:$begin_line-$end_line: \\\n$comment\n");
288 my $found_cplusplus_comment = sub {
292 if($options->comments_cplusplus) {
293 $output->write("$file:$line: C++ comments not allowed: $comment\n");
297 my $create_function = sub {
298 return 'winapi_function'->new;
301 my $found_function = sub {
302 my $function = shift;
304 my $internal_name = $function->internal_name;
305 $functions{$internal_name} = $function;
307 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
308 $output->prefix_callback(sub { return $function->prefix; });
310 my $declared_function = $declared_functions{$internal_name};
312 my $documentation_line = $function->documentation_line;
313 my $documentation = $function->documentation;
314 my $linkage = $function->linkage;
315 my $return_type = $function->return_type;
316 my $calling_convention = $function->calling_convention;
317 my $statements = $function->statements;
319 my $module16 = $function->module16;
320 my $module32 = $function->module32;
322 my $external_name = $function->external_name;
323 my $external_name16 = $function->external_name16;
324 my $external_name32 = $function->external_name32;
326 if(defined($external_name) && !defined($statements) &&
327 ($linkage eq "" || $linkage eq "extern"))
329 my $previous_function = $declared_functions{$internal_name};
330 if(!defined($previous_function)) {
331 $declared_functions{$internal_name} = $function;
333 my $file = $previous_function->file;
334 my $function_line = $previous_function->function_line;
337 $header =~ s%^(include|$file_dir)/%%;
338 if($header !~ m%^msvcrt/% || $file_dir =~ m%^dlls/msvcrt%) {
339 $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
344 if ($options->global) {
345 foreach my $module ($function->modules) {
346 $module2functions{$module}{$internal_name} = $function;
350 foreach my $module ($function->modules) {
351 $modules->found_module_in_dir($module, $file_dir);
354 if($options->shared) {
355 if($win16api->is_shared_internal_function($internal_name) ||
356 $win32api->is_shared_internal_function($internal_name))
358 $output->write("is shared between Win16 and Win32\n");
362 if($options->headers && $options->headers_needed &&
363 defined($declared_function) && defined($external_name) &&
364 defined($statements))
366 my $needed_include = $declared_function->file;
368 if(!defined($includes{$needed_include})) {
369 my $header = $needed_include;
370 $header =~ s%^(include|$file_dir)/%%;
371 if($header !~ m%^msvcrt/% || $file_dir =~ m%^dlls/msvcrt%) {
372 $output->write("prototype not included: #include \"$header\" is needed\n");
377 if($options->local && $options->argument && defined($statements)) {
378 &winapi_local::check_function($function);
381 if($options->local && $options->statements && defined($statements)) {
382 &winapi_local::check_statements(\%functions, $function);
385 if($options->local && $options->documentation &&
386 (defined($module16) || defined($module32)) &&
387 $linkage eq "" && defined($statements))
389 &winapi_documentation::check_documentation($function);
394 if(defined($external_name16)) {
395 $external_name16 = (split(/\s*&\s*/, $external_name16))[0];
399 if(defined($external_name32)) {
400 $external_name32 = (split(/\s*&\s*/, $external_name32))[0];
403 if($options->local && $options->misplaced &&
404 $linkage ne "extern" && defined($statements))
406 if($options->win16 && $options->report_module($module16))
408 if($file ne "library/port.c" &&
409 !$nativeapi->is_function($internal_name) &&
410 !is_subset($module16, $file_module16))
412 foreach my $module16 (split(/\s*&\s*/, $module16)) {
413 if(!$win16api->is_function_stub($module16, $internal_name)) {
414 $output->write("is misplaced ($module16)\n");
420 if($options->win32 && $options->report_module($module32))
422 if($file ne "library/port.c" &&
423 !$nativeapi->is_function($internal_name) &&
424 !is_subset($module32, $file_module32))
426 foreach my $module32 (split(/\s*&\s*/, $module32)) {
427 if(!$win32api->is_function_stub($module32, $internal_name)) {
428 $output->write("is misplaced ($module32)\n");
435 if($options->local && $options->headers && $options->prototype) {
436 if($options->win16 && $options->report_module($module16)) {
437 if(!$nativeapi->is_function($internal_name) &&
438 !defined($declared_functions{$internal_name}))
440 $output->write("no prototype\n");
444 if($options->win32 && $options->report_module($module32)) {
445 if(!defined($external_name32) || (!$nativeapi->is_function($external_name32) && !defined($declared_functions{$external_name32})))
447 if(!defined($external_name32) || ($external_name32 !~ /^Dll(?:
448 Install|CanUnloadNow|GetClassObject|GetVersion|
449 RegisterServer|RegisterServerEx|UnregisterServer)|DriverProc$/x &&
450 $internal_name !~ /^COMCTL32_Str/ &&
451 $internal_name !~ /^(?:\Q$module32\E|wine)_(?:\Q$external_name32\E|\d+)$/))
453 $output->write("no prototype\n");
465 my $found_include = sub {
467 if(/^\"(?:config\.h|wine\/port\.h)\"/) {
471 my $found_conditional = sub {
474 $nativeapi->found_conditional($_);
476 if($options->config) {
477 if(!$nativeapi->is_conditional($_)) {
478 if(/^HAVE_/ && !/^HAVE_(IPX|MESAGL|BUGGY_MESAGL|WINE_CONSTRUCTOR)$/)
480 $output->write("$file: $_ is not declared as a conditional\n");
485 $output->write("$file: conditional $_ used but config.h is not included\n");
491 my $create_type = sub {
495 my $found_type = sub {
499 my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
500 my $found_preprocessor = sub {
501 my $directive = shift;
502 my $argument = shift;
504 $preprocessor->directive($directive, $argument);
506 if($options->config) {
507 if($directive eq "include") {
509 my $check_protection;
511 if($argument =~ /^<(.*?)>$/) {
513 $check_protection = 1;
515 } elsif($argument =~ /^\"(.*?)\"$/) {
517 $check_protection = 0;
520 $output->write("$file: #$directive $argument: is unparsable\n");
523 $check_protection = 0;
527 if(defined($header)) {
529 if(-e "$wine_dir/include/$header") {
530 $include = "include/$header";
531 } elsif(-e "$file_dir/$header") {
532 $include = "$file_dir/$header";
533 } elsif(-e "$file_dir/../$header") {
534 if($file_dir =~ m%^(.*?)/[^/]+$%) {
535 $include = "$1/$header";
537 $include = "$header";
539 } elsif($header eq "controls.h") { # FIXME: Kludge
540 $include = "dlls/user/$header";
541 } elsif($header eq "ts_xlib.h") { # FIXME: Kludge
542 $include = "dlls/x11drv/$header";
543 } elsif($check_local) {
544 $output->write("$file: #include \"$header\": file not found\n");
547 if(defined($include)) {
548 $includes{$include}++;
549 foreach my $include (keys(%{$include2info{$include}{includes}})) {
550 $includes{$include}++;
555 if($check_protection && $header) {
556 if((-e "$wine_dir/include/$header" || -e "$wine_dir/$file_dir/$header")) {
557 if($header !~ /^(oleauto\.h|win(?:base|def|error|gdi|nls|nt|user)\.h)$/ &&
558 $file_dir !~ /tests$/)
560 $output->write("$file: #include \<$header\> is a local include\n");
564 my $macro = uc($header);
565 $macro =~ y/\.\//__/;
566 $macro = "HAVE_" . $macro;
568 if($nativeapi->is_conditional_header($header)) {
569 if(!$preprocessor->is_def($macro)) {
570 if($macro =~ /^HAVE_X11/) {
571 # Do nothing X Windows is handled differently
572 } elsif($macro =~ /^HAVE_(.*?)_H$/) {
574 if($header !~ /^alloca\.h$/ &&
575 $file_dir !~ /tests$/ &&
576 !$preprocessor->is_def("STATFS_DEFINED_BY_$name"))
578 $output->write("$file: #$directive $argument: is a conditional include, " .
579 "but is not protected\n");
583 } elsif($preprocessor->is_def($macro)) {
584 $output->write("$file: #$directive $argument: is protected, " .
585 "but is not a conditional include\n");
589 if($check_local && $header) {
590 if(-e "$file_dir/$header") {
591 if($file_dir ne ".") {
592 $include2info{"$file_dir/$header"}{used}++;
593 foreach my $name (keys(%{$include2info{"$file_dir/$header"}{includes}})) {
594 $include2info{$name}{used}++;
597 $include2info{"$header"}{used}++;
598 foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
599 $include2info{$name}{used}++;
602 } elsif(-e "$file_dir/../$header") {
603 if($file_dir =~ m%^(.*?)/[^/]+$%) {
604 $include2info{"$1/$header"}{used}++;
605 foreach my $name (keys(%{$include2info{"$1/$header"}{includes}})) {
606 $include2info{$name}{used}++;
609 $include2info{"$header"}{used}++;
610 foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
611 $include2info{$name}{used}++;
614 } elsif($header eq "controls.h") { # FIXME: Kludge
615 $include2info{"dlls/user/$header"}{used}++;
616 foreach my $name (keys(%{$include2info{"dlls/user/$header"}{includes}})) {
617 $include2info{$name}{used}++;
619 } elsif($header eq "ts_xlib.h") { # FIXME: Kludge
620 $include2info{"dlls/x11drv/$header"}{used}++;
621 foreach my $name (keys(%{$include2info{"dlls/user/$header"}{includes}})) {
622 $include2info{$name}{used}++;
624 } elsif(-e "$wine_dir/include/$header") {
625 $include2info{"include/$header"}{used}++;
626 foreach my $name (keys(%{$include2info{"include/$header"}{includes}})) {
627 $include2info{$name}{used}++;
630 $output->write("$file: #include \"$header\" is not a local include\n");
637 &winapi_parser::parse_c_file($file, {
638 c_comment_found => $found_c_comment,
639 cplusplus_comment_found => $found_cplusplus_comment,
640 function_create => $create_function,
641 function_found => $found_function,
642 type_create => $create_type,
643 type_found => $found_type,
644 preprocessor_found => $found_preprocessor
647 if($options->config_unnessary) {
648 if($config && $conditional == 0) {
649 $output->write("$file: include2info config.h but do not use any conditionals\n");
653 &winapi_local::check_file($file, \%functions);
656 if($options->global) {
657 &winapi_global::check_modules(\%complete_module, \%module2functions);
660 &winapi_global::check_all_modules(\%include2info);