Activate a hidden window only when explicitely asked by the
[wine] / tools / winapi_check / winapi_check
1 #!/usr/bin/perl -w
2
3 # Copyright 1999 Patrik Stridvall
4
5 use strict;
6
7 my $wine_dir;
8 my $winapi_check_dir;
9
10 BEGIN {
11
12     if($0 =~ /^((.*?)\/?tools\/winapi_check)\/winapi_check$/)
13     {
14         $winapi_check_dir = $1;
15         if($2 ne "")
16         {
17             $wine_dir = $2;
18         } else {
19             $wine_dir = ".";
20         }
21     }
22     @INC = ($winapi_check_dir);
23
24     require "nativeapi.pm";
25     require "output.pm";
26     require "preprocessor.pm";
27     require "winapi.pm";
28     require "winapi_function.pm";
29     require "winapi_local.pm";
30     require "winapi_global.pm";
31     require "winapi_options.pm";
32     require "winapi_parser.pm";
33
34     import nativeapi;
35     import output;
36     import preprocessor;
37     import winapi;
38     import winapi_function;
39     import winapi_local;
40     import winapi_global;
41     import winapi_options;
42     import winapi_parser;
43 }
44
45 my $options = winapi_options->new(\@ARGV);
46 if($options->help) {
47     $options->show_help;
48     exit;
49 }
50
51 my $output = 'output'->new;
52
53 my $win16api = 'winapi'->new($output, "win16", "$winapi_check_dir/win16");
54 my $win32api = 'winapi'->new($output, "win32", "$winapi_check_dir/win32");
55 'winapi'->read_spec_files($wine_dir, $win16api, $win32api);
56
57 my $nativeapi = 'nativeapi'->new($output, "$winapi_check_dir/nativeapi.dat", "$wine_dir/configure.in", "$wine_dir/include/config.h.in");
58
59 for my $name ($win32api->all_functions) {
60     my $module16 = $win16api->function_module($name);
61     my $module32 = $win32api->function_module($name);
62         
63     if(defined($module16)) {
64         $win16api->found_shared_function($name);
65         $win32api->found_shared_function($name);
66         
67         if($options->shared) {
68             $output->write("*.spec: $name: is shared between $module16 (Win16) and $module32 (Win32)\n");
69         }
70     }
71 }
72
73 my %includes;
74 {   
75     my @files = map {
76         s/^.\/(.*)$/$1/;
77         $_; 
78     } split(/\n/, `find . -name \\*.h`);
79     
80     foreach my $file (@files) {
81         my $file_dir = $file;
82         if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
83             $file_dir = ".";
84         }
85    
86         $includes{$file} = { name => $file };
87
88         open(IN, "< $file");
89         while(<IN>) {
90             if(/^\s*\#\s*include\s*\"(.*?)\"/) {
91                 my $header = $1;
92                 if(-e "$file_dir/$header") {
93                     $includes{$file}{includes}{"$file_dir/$header"}++;
94                 } elsif(-e "$wine_dir/include/$header") {
95                     $includes{$file}{includes}{"include/$header"}++;
96                 } else {                   
97                     $output->write("$file: #include \"$header\" is not a local include ($file_dir)\n");
98                 }
99             }
100         }
101         close(IN);
102     }
103
104     my @files2 = ("acconfig.h", "poppack.h", "pshpack1.h", "pshpack2.h", "pshpack4.h", "pshpack8.h",
105                   "storage.h", "ver.h");
106     foreach my $file2 (@files2) {
107         $includes{"include/$file2"}{used}++;
108     }    
109 }
110
111 my $progress_output;
112 my $progress_current=0;
113 my $progress_max=scalar($options->files);
114 foreach my $file ($options->files) {
115     my %functions;
116
117     $progress_current++;
118     if($options->progress) {
119         $output->progress("$file: file $progress_current of $progress_max");
120     }
121
122     my $file_dir = $file;
123     if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
124         $file_dir = ".";
125     }
126
127     my $file_type;
128     if($file_dir =~ /^(libtest|program|rc|tools)/ || 
129        $file =~ /dbgmain\.c$/ ||
130        $file =~ /wineclipsrv\.c$/) # FIXME: Kludge
131     {
132         $file_type = "application";
133     } elsif($file_dir =~ /^(debug|miscemu)/) {
134         $file_type = "emulator";
135     } else {
136         $file_type = "library";
137     }
138     
139     my $found_function = sub {
140         my $documentation = shift;
141         my $linkage = shift;
142         my $return_type = shift;
143         my $calling_convention = shift;
144         my $name = shift;
145         my $refarguments = shift;
146         my @arguments = @$refarguments;
147         my $statements = shift;
148
149         if($options->global) {
150             $win16api->found_type($return_type) if $options->win16;
151             $win32api->found_type($return_type) if $options->win32;
152             for my $argument (@arguments) {
153                 $win16api->found_type($argument) if $options->win16;
154                 $win32api->found_type($argument) if $options->win32;
155             }
156             
157             $win16api->found_function($name) if $options->win16;
158             $win32api->found_function($name) if $options->win32;
159         }
160         
161         if($options->local && $file_type ne "application") {
162             my $module16 = $win16api->function_module($name);
163             my $module32 = $win32api->function_module($name);
164
165             my $function = 'winapi_function'->new;
166             $functions{$name} = $function;
167            
168             $function->documentation($documentation);
169             $function->linkage($linkage);
170             $function->file($file);
171             $function->return_type($return_type); 
172             $function->calling_convention($calling_convention);
173             $function->name($name); 
174             $function->arguments([@arguments]);
175             $function->statements($statements);
176             $function->module16($module16);
177             $function->module32($module32);
178
179             my $output_module = sub { 
180                 my $module = shift;
181                 return sub {
182                     my $msg = shift;
183                     $output->write("$file: $module: $return_type ");
184                     $output->write("$calling_convention ") if $calling_convention;
185                     $output->write("$name(" . join(",", @arguments) . "): $msg\n");
186                 }
187             };
188             my $output16 = &$output_module($module16);
189             my $output32 = &$output_module($module32);
190             
191             if($options->argument) {
192                 if($options->win16 && $options->report_module($module16)) {
193                   winapi_local::check_function $options, $output16,
194                     $return_type, $calling_convention, $name, [@arguments], $win16api;
195                 }       
196                 if($options->win32 && $options->report_module($module32)) {
197                   winapi_local::check_function $options, $output32,
198                     $return_type, $calling_convention, $name, [@arguments], $win32api;
199                 }
200             }
201             if($options->misplaced) {
202                 my $module; 
203                 if($file =~ m'^dlls/(.*)/') {
204                     $module = $1;
205                 }
206
207                 if($options->win16 && $options->report_module($module16)) {
208                     if(!defined($module) || $module ne $module16) {
209                         &$output16("function misplaced");
210                     }
211                 }
212                 
213                 if($options->win32 && $options->report_module($module32)) {
214                     if(!defined($module) || $module ne $module32) {
215                         &$output32("function misplaced");
216                     }
217                 }               
218             }
219             if($options->cross_call) {
220                 local $_ = $statements; 
221                 my $called_function_names = {};
222                 while(defined($_)) {
223                     if(/(\w+)\((.*?)\)/) {
224                         $_ = $';
225                         my $called_name = $1;
226                         if($called_name !~ /^if|for|while|switch|sizeof$/) {
227                             $functions{$name}->function_called($called_name);
228                             if(!defined($functions{$called_name})) {
229                                 $functions{$called_name} = 'function'->new;
230                             }
231                             $functions{$called_name}->function_called_by($name);
232                         }
233                     } else {
234                        undef $_
235                     }
236                 }
237             }
238             
239             if($options->documentation && (defined($module16) || defined($module32)) && 
240                $linkage ne "extern" && $statements ne "") 
241             {           
242                 my $name1;
243                 my $name2;
244                 my $name3;
245                 my $name4;
246                 my $name5;
247
248                 if(defined($module16) && !defined($module32)) {
249                     my @uc_modules16 = split(/\s*\&\s*/, uc($module16));
250                     push @uc_modules16, "WIN16";
251
252                     $name1 = $name;
253                     foreach my $uc_module16 (@uc_modules16) {
254                         if($name1 =~ s/^$uc_module16\_//) { last; }
255                     }
256
257                     $name2 = $name1;
258                     $name2 =~ s/([AW])$/16$1/;
259
260                     $name3 = $name1;
261                     $name3 =~ s/16(([AW])?)$/$1/;
262
263                     $name4 = $name1;
264                     $name4 =~ s/^(.*?)(?:16)?$/\U$1\E/;
265
266                     $name5 = $name1;
267                     $name5 = s/^(.*?)16_fn(.*?)$/$116_$2/;
268                 } elsif(!defined($module16) && defined($module32)) {
269                     my @uc_modules32 = split(/\s*\&\s*/, uc($module32));
270
271                     $name1 = $name;
272                     foreach my $uc_module32 (@uc_modules32) {
273                         if($name1 =~ s/^$uc_module32\_//) { last; }
274                     }
275
276                     $name2 = $name1;
277                     $name2 =~ s/([AW])$/32$1/;
278
279                     $name3 = $name1;
280                     $name3 =~ s/32(([AW])?)$/$1/;
281
282                     $name4 = $name1;
283                     $name4 =~ s/AW$//;
284
285                     $name5 = $name1;
286                 } else {
287                     my @uc_modules = split(/\s*\&\s*/, uc($module16));
288                     push @uc_modules, split(/\s*\&\s*/, uc($module32));
289
290                     $name1 = $name;
291                     foreach my $uc_module (@uc_modules) {
292                         if($name1 =~ s/^$uc_module\_//) { last; }
293                     }
294
295                     $name2 = $name1;
296
297                     $name3 = $name1;
298
299                     $name4 = $name1;
300
301                     $name5 = $name1;
302                 }
303
304                 if($name !~ /^SMapLS|SUnMapLS/ && $documentation !~ /($name|$name1|$name2|$name3|$name4|$name5)/) {
305                     $output->write("$file: $name: \\\n");
306                     $output->write("$documentation\n");
307                 }              
308             }
309
310         }
311     };
312
313     my $config = 0;
314     my $conditional = 0;
315     my $found_include = sub {
316         local $_ = shift;
317         if(/^\"config\.h\"/) {
318             $config++;
319         }
320     };
321     my $found_conditional = sub {
322         if($file_type ne "application") {
323            local $_ = shift;
324            if(!$nativeapi->is_conditional($_)) {
325                if(/^HAVE_/ && !/^HAVE_(IPX|MESAGL|BUGGY_MESAGL|WINE_CONSTRUCTOR)$/)
326                {
327                    $output->write("$file: $_ is not declared as a conditional\n");
328                }
329            } else {
330                $conditional++;
331                if(!$config) {
332                    $output->write("$file: conditional $_ used but config.h is not included\n");
333                }
334            }
335        }
336     };
337     my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
338     my $found_preprocessor = sub {
339         my $directive = shift;
340         my $argument = shift;
341
342         $preprocessor->directive($directive, $argument);
343
344         if($options->config) {
345             if($directive eq "include") {
346                 my $header;
347                 my $check_protection;
348                 my $check_local;
349                 if($argument =~ /^<(.*?)>$/) {
350                    $header = $1;
351                    if($file_type ne "application") {
352                        $check_protection = 1;
353                    } else {
354                        $check_protection = 0;
355                    }
356                    $check_local = 0;
357                 } elsif($argument =~ /^"(.*?)"$/) {
358                    $header = $1;
359                    $check_protection = 0;
360                    $check_local = 1;
361                 }
362
363                 if($check_protection) {
364                     if((-e "$wine_dir/include/$header" || -e "$file_dir/$header")) {
365                         if($header !~ /^ctype.h$/) {
366                             $output->write("$file: #include \<$header\> is a local include\n");
367                         }
368                     }
369
370                     my $macro = uc($header);
371                     $macro =~ y/\.\//__/;
372                     $macro = "HAVE_" . $macro;
373                     
374                     if($nativeapi->is_conditional_header($header)) { 
375                         if(!$preprocessor->is_def($macro)) {
376                             if($macro =~ /^HAVE_X11/) {
377                                 if(!$preprocessor->is_undef("X_DISPLAY_MISSING")) {
378                                     $output->write("$file: #$directive $argument: is a conditional include, " .
379                                                    "but is not protected\n");
380                                 }
381                             } elsif($macro =~ /^HAVE_(.*?)_H$/) {
382                                 if($header ne "alloca.h" && !$preprocessor->is_def("STATFS_DEFINED_BY_$1")) {
383                                     $output->write("$file: #$directive $argument: is a conditional include, " . 
384                                                    "but is not protected\n");
385                                 }
386                             }
387                         }
388                     } elsif($preprocessor->is_def($macro)) {
389                         $output->write("$file: #$directive $argument: is protected, " .
390                                        "but is not a conditional include\n");
391                     }
392                 }
393
394                 if($check_local) {
395                     if(-e "$file_dir/$header") {
396                         $includes{"$file_dir/$header"}{used}++;
397                         foreach my $name (keys(%{$includes{"$file_dir/$header"}{includes}})) {
398                             $includes{$name}{used}++;
399                         }
400                     } elsif(-e "$wine_dir/include/$header") {
401                         $includes{"include/$header"}{used}++;
402                         foreach my $name (keys(%{$includes{"include/$header"}{includes}})) {
403                             $includes{$name}{used}++;
404                         }
405                     } else {
406                         $output->write("$file: #include \"$header\" is not a local include\n");
407                     }
408                 }
409             }
410         }
411     };
412   
413     winapi_parser::parse_c_file $options, $output, $file, $found_function, $found_preprocessor;
414     
415     if($options->config_unnessary) {
416         if($config && $conditional == 0) {
417             $output->write("$file: includes config.h but do not use any conditionals\n");
418         }
419     }
420
421     winapi_local::check_file $options, $output, $file, \%functions;
422 }
423
424 $output->hide_progress;
425
426 if($options->global) {
427     foreach my $name (sort(keys(%includes))) {
428         if(!$includes{$name}{used}) {
429             if($options->include) {
430                 $output->write("$name: include file is never used\n");
431             }
432         }
433     }
434
435     winapi_global::check $options, $output, $win16api, $nativeapi if $options->win16;
436     winapi_global::check $options, $output, $win32api, $nativeapi if $options->win32;
437 }
438