- Updated API files
[wine] / tools / winapi_check / winapi_options.pm
1 package winapi_options;
2
3 use strict;
4
5 sub parser_comma_list {
6     my $prefix = shift;
7     my $value = shift;
8     if(defined($prefix) && $prefix eq "no") {
9         return { active => 0, filter => 0, hash => {} };
10     } elsif(defined($value)) {
11         my %names;
12         for my $name (split /,/, $value) {
13             $names{$name} = 1;
14         }
15         return { active => 1, filter => 1, hash => \%names };
16     } else {
17         return { active => 1, filter => 0, hash => {} };
18     }
19 }
20
21 my %options = (
22     "debug" => { default => 0, description => "debug mode" },
23     "help" => { default => 0, description => "help mode" },
24     "verbose" => { default => 0, description => "verbose mode" },
25
26     "progress" => { default => 1, description => "show progress" },
27
28     "win16" => { default => 1, description => "Win16 checking" },
29     "win32" => { default => 1, description => "Win32 checking" },
30
31     "shared" =>  { default => 0, description => "show shared functions between Win16 and Win32" },
32     "shared-segmented" =>  { default => 0, description => "segmented shared functions between Win16 and Win32 checking" },
33
34     "config" => { default => 1, description => "check configuration include consistancy" },
35     "config-unnessary" => { default => 0, parent => "config", description => "check for unnessary #include \"config.h\"" },
36
37     "spec-mismatch" => { default => 0, description => "spec file mismatch checking" },
38
39     "local" =>  { default => 1, description => "local checking" },
40     "module" => { 
41         default => { active => 1, filter => 0, hash => {} },
42         parent => "local",
43         parser => \&parser_comma_list,
44         description => "module filter"
45     },
46
47     "argument" => { default => 1, parent => "local", description => "argument checking" },
48     "argument-count" => { default => 1, parent => "argument", description => "argument count checking" },
49     "argument-forbidden" => {
50         default => { active => 1, filter => 0, hash => {} },
51         parent => "argument",
52         parser => \&parser_comma_list,
53         description => "argument forbidden checking"
54     },
55     "argument-kind" => {
56         default => { active => 0, filter => 0, hash => {} },
57         parent => "argument",
58         parser => \&parser_comma_list,
59         description => "argument kind checking"
60     },
61     "calling-convention" => { default => 0, parent => "local", description => "calling convention checking" },
62     "misplaced" => { default => 1, parent => "local", description => "check for misplaced functions" },
63     "statements"  => { default => 0, parent => "local", description => "check for statements inconsistances" },
64     "cross-call" => { default => 0, parent => "statements",  description => "check for cross calling functions" },
65     "cross-call-win32-win16" => { 
66         default => 0, parent => "cross-call", description => "check for cross calls between win32 and win16"
67      },
68     "cross-call-unicode-ascii" => { 
69         default => 0, parent => "cross-call", description => "check for cross calls between Unicode and ASCII" 
70      },
71     "debug-messages" => { default => 0, parent => "statements", description => "check for debug messages inconsistances" },
72     "documentation" => { default => 1, parent => "local", description => "check for documentation inconsistances\n" },
73     "documentation-width" => { default => 0, parent => "documentation", description => "check for documentation width inconsistances\n" },
74     "prototype" => { default => 0, parent => ["local", "headers"], description => "prototype checking" },
75
76     "global" => { default => 1, description => "global checking" },
77     "declared" => { default => 1, parent => "global", description => "declared checking" },
78     "implemented" => { default => 1, parent => "global", description => "implemented checking" },
79     "implemented-win32" => { default => 0, parent => "implemented", description => "implemented as win32 checking" },
80     "include" => { default => 1, parent => "global", description => "include checking" },
81     "headers" => { default => 0, parent => "global", description => "headers checking" },
82     "headers-duplicated" => { default => 0, parent => "headers", description => "duplicated function declarations checking" },
83     "headers-misplaced" => { default => 0, parent => "headers", description => "misplaced function declarations checking" },
84     "stubs" => { default => 0, parent => "global", description => "stubs checking" }
85 );
86
87 my %short_options = (
88     "d" => "debug",
89     "?" => "help",
90     "v" => "verbose"
91 );
92
93 sub new {
94     my $proto = shift;
95     my $class = ref($proto) || $proto;
96     my $self  = {};
97     bless ($self, $class);
98
99     my $output = \${$self->{OUTPUT}};
100
101     $$output = shift;
102     my $refarguments = shift;
103     my $wine_dir = shift;
104
105     $self->options_set("default");
106
107     my $c_files = \@{$self->{C_FILES}};
108     my $h_files = \@{$self->{H_FILES}};
109     my $module = \${$self->{MODULE}};
110     my $global = \${$self->{GLOBAL}};
111
112     my @files;
113
114     if($wine_dir eq ".") {
115         $$global = 1;
116     } else {
117         $$global = 0;
118     }
119
120     while(defined($_ = shift @$refarguments)) {
121         if(/^--(all|none)$/) {
122             $self->options_set("$1");
123             next;
124         } elsif(/^-([^=]*)(=(.*))?$/) {
125             my $name;
126             my $value;
127             if(defined($2)) {
128                 $name = $1;
129                 $value = $3;
130             } else {
131                 $name = $1;
132             }
133             
134             if($name =~ /^([^-].*)$/) {
135                 $name = $short_options{$1};
136             } else {
137                 $name =~ s/^-(.*)$/$1/;
138             }
139                    
140             my $prefix;
141             if(defined($name) && $name =~ /^no-(.*)$/) {
142                 $name = $1;
143                 $prefix = "no";
144                 if(defined($value)) {
145                     $$output->write("options with prefix 'no' can't take parameters\n");
146
147                     return undef;
148                 }
149             }
150
151             my $option;
152             if(defined($name)) {
153                 $option = $options{$name};
154             }
155
156             if(defined($option)) {
157                 my $key = $$option{key};
158                 my $parser = $$option{parser};
159                 my $refvalue = \${$self->{$key}};
160                 my @parents = ();
161                 
162                 if(defined($$option{parent})) {
163                     if(ref($$option{parent}) eq "ARRAY") {
164                         @parents = @{$$option{parent}};
165                     } else {
166                         @parents = $$option{parent};
167                     }
168                 }
169
170                 if(defined($parser)) { 
171                     $$refvalue = &$parser($prefix,$value);
172                 } else {
173                     if(defined($value)) {
174                         $$refvalue = $value;
175                     } elsif(!defined($prefix)) {
176                         $$refvalue = 1;
177                     } else {
178                         $$refvalue = 0;
179                     }
180                 }
181
182                 if((ref($$refvalue) eq "HASH" && $$refvalue->{active}) || $$refvalue) {
183                     while($#parents >= 0) {
184                         my @old_parents = @parents;
185                         @parents = ();
186                         foreach my $parent (@old_parents) {
187                             my $parentkey = $options{$parent}{key};
188                             my $refparentvalue = \${$self->{$parentkey}};
189                             
190                             $$refparentvalue = 1;
191
192                             if(defined($options{$parent}{parent})) {
193                                 if(ref($options{$parent}{parent}) eq "ARRAY") {
194                                     push @parents, @{$options{$parent}{parent}};
195                                 } else {
196                                     push @parents, $options{$parent}{parent};
197                                 }
198                             }
199                         }
200                     }
201                 }
202                 next;
203             }    
204         }
205         
206         if(/^--module-dlls$/) {
207             my @dirs = `cd dlls && find . -type d ! -name CVS`;
208             my %names;
209             for my $dir (@dirs) {
210                 chomp $dir;
211                 $dir =~ s/^\.\/(.*)$/$1/;
212                 next if $dir eq "";
213                 $names{$dir} = 1;
214             }
215             $$module = { active => 1, filter => 1, hash => \%names };
216         }       
217         elsif(/^-(.*)$/) {
218             $$output->write("unknown option: $_\n"); 
219
220             return undef;
221         } else {
222             if(!-e $_) {
223                 $$output->write("$_: no such file or directory\n");
224
225                 return undef;
226             }
227
228             push @files, $_;
229         }
230     }
231
232     if($self->help) {
233         return $self;
234     }
235
236     my @paths = ();
237     my @c_files = ();
238     my @h_files = ();
239     foreach my $file (@files) {
240         if($file =~ /\.c$/) {
241             push @c_files, $file;
242         } elsif($file =~ /\.h$/) {
243             push @h_files, $file;
244         } else {
245             push @paths, $file;
246         }
247     }
248
249     if($#c_files == -1 && $#h_files == -1 &&
250        ($#paths == -1 || ($#paths == 0 && $paths[0] eq $wine_dir)))
251     {
252         @paths = ".";
253         push @h_files, "$wine_dir/include";
254     } else {
255         $$global = 0;
256     }
257
258     if($#paths != -1 || $#c_files != -1) {
259         my $c_command = "find " . join(" ", @paths, @c_files) . " -name \\*.c";
260         my %found;
261         @$c_files = sort(map {
262             s/^\.\/(.*)$/$1/;
263             if(defined($found{$_}) || /glue\.c|spec\.c$/) {
264                 ();
265             } else {
266                 $found{$_}++;
267                 $_;
268             }
269         } split(/\n/, `$c_command`));
270     }
271
272     if($#h_files != -1) {
273         my $h_command = "find " . join(" ", @h_files) . " -name \\*.h";
274         my %found;
275
276         @$h_files = sort(map {
277             s/^\.\/(.*)$/$1/;
278             if(defined($found{$_})) {
279                 ();
280             } else {
281                 $found{$_}++;
282                 $_;
283             }
284         } split(/\n/, `$h_command`));
285     }
286     return $self;
287 }
288
289 sub options_set {
290     my $self = shift;
291
292     local $_ = shift;
293     for my $name (sort(keys(%options))) {
294         my $option = $options{$name};
295         my $key = uc($name);
296         $key =~ tr/-/_/;
297         $$option{key} = $key;
298         my $refvalue = \${$self->{$key}};
299
300         if(/^default$/) {
301             $$refvalue = $$option{default};
302         } elsif(/^all$/) {
303             if($name !~ /^help|debug|verbose|module$/) {
304                 if(ref($$refvalue) ne "HASH") {
305                     $$refvalue = 1;
306                 } else {
307                     $$refvalue = { active => 1, filter => 0, hash => {} };
308                 }
309             }
310         } elsif(/^none$/) {
311             if($name !~ /^help|debug|verbose|module$/) {
312                 if(ref($$refvalue) ne "HASH") {
313                     $$refvalue = 0;
314                 } else {
315                     $$refvalue = { active => 0, filter => 0, hash => {} };
316                 }
317             }
318         }
319     }
320 }
321
322 sub show_help {
323     my $self = shift;
324
325     my $maxname = 0;
326     for my $name (sort(keys(%options))) {
327         if(length($name) > $maxname) {
328             $maxname = length($name);
329         }
330     }
331
332     print "usage: winapi-check [--help] [<files>]\n";
333     print "\n";
334     for my $name (sort(keys(%options))) {
335         my $option = $options{$name};
336         my $description = $$option{description};
337         my $default = $$option{default};
338         my $current = ${$self->{$$option{key}}};
339
340         my $value = $current;
341         
342         my $output;
343         if(ref($value) ne "HASH") {
344             if($value) {
345                 $output = "--no-$name";
346             } else {
347                 $output = "--$name";
348             }
349         } else {
350             if($value->{active}) {
351                 $output = "--[no-]$name\[=<value>]";
352             } else {
353                 $output = "--$name\[=<value>]";
354             }
355         }
356
357         print "$output";
358         for (0..(($maxname - length($name) + 17) - (length($output) - length($name) + 1))) { print " "; }
359         if(ref($value) ne "HASH") {
360             if($value) {
361                 print "Disable ";
362             } else {
363                 print "Enable ";
364             }    
365         } else {
366             if($value->{active}) {
367                 print "(Disable) ";
368             } else {
369                 print "Enable ";
370             }
371         }
372         if($default == $current) {
373             print "$description (default)\n";
374         } else {
375             print "$description\n";
376         }    
377     }
378 }
379
380 sub AUTOLOAD {
381     my $self = shift;
382
383     my $name = $winapi_options::AUTOLOAD;
384     $name =~ s/^.*::(.[^:]*)$/\U$1/;
385
386     my $refvalue = $self->{$name};
387     if(!defined($refvalue)) {
388         die "<internal>: winapi_options.pm: member $name does not exists\n"; 
389     }
390
391     if(ref($$refvalue) ne "HASH") {
392         return $$refvalue;
393     } else {
394         return $$refvalue->{active};
395     }
396 }
397
398 sub c_files { my $self = shift; return @{$self->{C_FILES}}; }
399
400 sub h_files { my $self = shift; return @{$self->{H_FILES}}; }
401
402 sub report_module {
403     my $self = shift;
404     my $refvalue = $self->{MODULE};
405     
406     my $name = shift;
407
408     if(defined($name)) {
409         return $$refvalue->{active} && (!$$refvalue->{filter} || $$refvalue->{hash}->{$name}); 
410     } else {
411         return 0;
412     } 
413 }
414
415 sub report_argument_forbidden {
416     my $self = shift;   
417     my $refargument_forbidden = $self->{ARGUMENT_FORBIDDEN};
418
419     my $type = shift;
420
421     return $$refargument_forbidden->{active} && (!$$refargument_forbidden->{filter} || $$refargument_forbidden->{hash}->{$type}); 
422 }
423
424 sub report_argument_kind {
425     my $self = shift;
426     my $refargument_kind = $self->{ARGUMENT_KIND};
427
428     my $kind = shift;
429
430     return $$refargument_kind->{active} && (!$$refargument_kind->{filter} || $$refargument_kind->{hash}->{$kind}); 
431
432 }
433
434 1;
435