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