Review and fix regular expressions of the form /^foo|bar$/.
[wine] / tools / winapi_check / modules.pm
1 #
2 # Copyright 1999, 2000, 2001 Patrik Stridvall
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 #
18
19 package modules;
20
21 use strict;
22
23 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
24 require Exporter;
25
26 @ISA = qw(Exporter);
27 @EXPORT = qw();
28 @EXPORT_OK = qw($modules);
29
30 use vars qw($modules);
31
32 use config qw(
33     file_type files_skip
34     file_directory
35     get_c_files get_spec_files
36     $current_dir $wine_dir
37     $winapi_check_dir
38 );
39 use options qw($options);
40 use output qw($output);
41
42 sub import(@) {
43     $Exporter::ExportLevel++;
44     Exporter::import(@_);
45     $Exporter::ExportLevel--;
46
47     if (defined($modules)) {
48         return;
49     }
50
51     $modules = 'modules'->new;
52 }
53
54 sub get_spec_file_type($) {
55     my $file = shift;
56
57     my $module;
58     my $type;
59
60     $module = $file;
61     $module =~ s%^.*?([^/]+)\.spec$%$1%;
62
63     open(IN, "< $file") || die "$file: $!\n";
64     local $/ = "\n";
65     my $header = 1;
66     my $lookahead = 0;
67     while($lookahead || defined($_ = <IN>)) {
68         $lookahead = 0;
69         s/^\s*(.*?)\s*$/$1/;
70         s/^(.*?)\s*#.*$/$1/;
71         /^$/ && next;
72
73         if($header)  {
74             if(/^(?:\d+|@)/) { $header = 0; $lookahead = 1; }
75             next;
76         }
77
78         if(/^(\d+|@)\s+pascal(?:16)?/) {
79             $type = "win16";
80             last;
81         }
82     }
83     close(IN);
84
85     if(!defined($type)) {
86         $type = "win32";
87     }
88
89     return ($type, $module);
90 }
91
92 sub find_spec_files($) {
93     my $self = shift;
94
95     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
96     my $spec_file2dir = \%{$self->{SPEC_FILE2DIR}};
97
98     $output->progress("modules");
99
100     my $spec_file_found = {};
101     my $allowed_dir;
102     my $spec_file;
103
104     my @spec_files = <{dlls/*/*.spec,dlls/*/*/*.spec}>;
105
106     foreach $spec_file (@spec_files) {
107         $spec_file =~ /(.*)\/.*\.spec/;
108
109         $allowed_dir = $1;
110
111         $$spec_file_found{$spec_file}++;
112         $$spec_file2dir{$spec_file}{$allowed_dir}++;
113         $$dir2spec_file{$allowed_dir}{$spec_file}++;
114     }
115
116     return $spec_file_found;
117 }
118
119 sub read_spec_files($$) {
120     my $self = shift;
121
122     my $spec_file_found = shift;
123
124     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
125     my $spec_files16 = \@{$self->{SPEC_FILES16}};
126     my $spec_files32 = \@{$self->{SPEC_FILES32}};
127     my $spec_file2module = \%{$self->{SPEC_FILE2MODULE}};
128     my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
129
130     my @spec_files;
131     if($wine_dir eq ".") {
132         @spec_files = get_spec_files("winelib");
133     } else {
134         my %spec_files = ();
135         foreach my $dir ($options->directories) {
136             $dir = "$current_dir/$dir";
137             $dir =~ s%/\.$%%;
138             foreach my $spec_file (sort(keys(%{$$dir2spec_file{$dir}}))) {
139                 $spec_files{$spec_file}++;
140             }
141         }
142         @spec_files = sort(keys(%spec_files));
143     }
144
145     @$spec_files16 = ();
146     @$spec_files32 = ();
147     foreach my $spec_file (@spec_files) {
148         (my $type, my $module) = get_spec_file_type("$wine_dir/$spec_file");
149
150         $$spec_file2module{$spec_file} = $module;
151         $$module2spec_file{$module} = $spec_file;
152
153         if($type eq "win16") {
154             push @$spec_files16, $spec_file;
155         } elsif($type eq "win32") {
156             push @$spec_files32, $spec_file;
157         } else {
158             $output->write("$spec_file: unknown type '$type'\n");
159         }
160     }
161
162     foreach my $spec_file (@spec_files) {
163         if(!$$spec_file_found{$spec_file} && $spec_file !~ m%tests/[^/]+$%) {
164             $output->write("modules: $spec_file: exists but is not specified\n");
165         }
166     }
167 }
168
169 sub new($) {
170     my $proto = shift;
171     my $class = ref($proto) || $proto;
172     my $self  = {};
173     bless ($self, $class);
174
175     my $spec_file_found = $self->find_spec_files();
176     $self->read_spec_files($spec_file_found);
177
178     return $self;
179 }
180
181 sub all_modules($) {
182     my $self = shift;
183
184     my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
185
186     return sort(keys(%$module2spec_file));
187 }
188
189 sub complete_modules($$) {
190     my $self = shift;
191
192     my $c_files = shift;
193
194     my %dirs;
195
196     foreach my $file (@$c_files) {
197         my $dir = file_directory("$current_dir/$file");
198         $dirs{$dir}++;
199     }
200
201     my @c_files = get_c_files("winelib");
202     @c_files = files_skip(@c_files);
203     foreach my $file (@c_files) {
204         my $dir = file_directory($file);
205         if(exists($dirs{$dir})) {
206             $dirs{$dir}--;
207         }
208     }
209
210     my @complete_modules = ();
211     foreach my $module ($self->all_modules) {
212         my $index = -1;
213         my @dirs = $self->allowed_dirs_for_module($module);
214         foreach my $dir (@dirs) {
215             if(exists($dirs{$dir}) && $dirs{$dir} == 0) {
216                 $index++;
217             }
218         }
219         if($index == $#dirs) {
220             push @complete_modules, $module;
221         }
222     }
223
224     return @complete_modules;
225 }
226
227 sub is_allowed_module($$) {
228     my $self = shift;
229
230     my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
231
232     my $module = shift;
233
234     return defined($$module2spec_file{$module});
235 }
236
237 sub is_allowed_module_in_file($$$) {
238     my $self = shift;
239
240     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
241     my $spec_file2module = \%{$self->{SPEC_FILE2MODULE}};
242
243     my $module = shift;
244     my $file = shift;
245     $file =~ s/^\.\///;
246
247     my $dir = $file;
248     $dir =~ s/\/[^\/]*$//;
249
250     if($dir =~ m%^include%) {
251         return 1;
252     }
253
254     foreach my $spec_file (sort(keys(%{$$dir2spec_file{$dir}}))) {
255         if($$spec_file2module{$spec_file} eq $module) {
256             return 1;
257         }
258     }
259
260     return 0;
261 }
262
263 sub allowed_modules_in_file($$) {
264     my $self = shift;
265
266     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
267     my $spec_file2module = \%{$self->{SPEC_FILE2MODULE}};
268
269     my $file = shift;
270     $file =~ s/^\.\///;
271
272     my $dir = $file;
273     $dir =~ s/\/[^\/]*$//;
274
275     my %allowed_modules = ();
276     foreach my $spec_file (sort(keys(%{$$dir2spec_file{$dir}}))) {
277         my $module = $$spec_file2module{$spec_file};
278         $allowed_modules{$module}++;
279     }
280
281     my $module = join(" & ", sort(keys(%allowed_modules)));
282
283     return $module;
284 }
285
286 sub allowed_dirs_for_module($$) {
287    my $self = shift;
288
289    my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
290    my $spec_file2dir = \%{$self->{SPEC_FILE2DIR}};
291
292    my $module = shift;
293
294    my $spec_file = $$module2spec_file{$module};
295
296    return sort(keys(%{$$spec_file2dir{$spec_file}}));
297 }
298
299 sub allowed_spec_files16($) {
300     my $self = shift;
301
302     my $spec_files16 = \@{$self->{SPEC_FILES16}};
303
304     return @$spec_files16;
305 }
306
307 sub allowed_spec_files32($) {
308     my $self = shift;
309
310     my $spec_files32 = \@{$self->{SPEC_FILES32}};
311
312     return @$spec_files32;
313 }
314
315 sub found_module_in_dir($$$) {
316     my $self = shift;
317
318     my $module = shift;
319     my $dir = shift;
320
321     my $used_module_dirs = \%{$self->{USED_MODULE_DIRS}};
322
323     $dir = "$current_dir/$dir";
324     $dir =~ s%/\.$%%;
325
326     $$used_module_dirs{$module}{$dir}++;
327 }
328
329 sub global_report($) {
330     my $self = shift;
331
332     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
333     my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
334     my $used_module_dirs = \%{$self->{USED_MODULE_DIRS}};
335
336     my @messages;
337     foreach my $dir ($options->directories) {
338         $dir = "$current_dir/$dir";
339         $dir =~ s%/\.$%%;
340         foreach my $module ($self->all_modules) {
341             if(!$$used_module_dirs{$module}{$dir}) {
342                 my $spec_file = $$module2spec_file{$module};
343                 push @messages, "modules: $spec_file: directory ($dir) is not used\n";
344             }
345         }
346     }
347
348     foreach my $message (sort(@messages)) {
349         $output->write($message);
350     }
351 }
352
353 1;