A few bug fixes.
[wine] / tools / winapi_check / modules.pm
1 package modules;
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();
10 @EXPORT_OK = qw($modules);
11
12 use vars qw($modules);
13
14 use config qw(
15     &file_type &files_skip
16     &file_directory
17     &get_c_files &get_spec_files 
18     $current_dir $wine_dir
19     $winapi_check_dir
20 );
21 use options qw($options);
22 use output qw($output);
23
24 $modules = 'modules'->new;
25
26 sub get_spec_file_type {
27     my $file = shift;
28
29     my $module;
30     my $type;
31
32     open(IN, "< $file") || die "$file: $!\n";
33     local $/ = "\n";
34     while(<IN>) {
35         s/^\s*(.*?)\s*$/$1/;
36         s/^(.*?)\s*#.*$/$1/;
37         /^$/ && next;
38
39         if(/^name\s*(\S*)/) { $module = $1; }
40         if(/^type\s*(\w+)/) { $type = $1; }
41
42         if(defined($module) && defined($type)) { last; }
43     }
44     close(IN);
45
46     if(!defined($module)) {
47         $module = $file;
48         $module =~ s%^.*?([^/]+)\.spec|%$1%;
49     }
50
51     if(!defined($type)) {
52         $type = "";
53     }
54
55     return ($type, $module);
56 }
57
58 sub new {
59     my $proto = shift;
60     my $class = ref($proto) || $proto;
61     my $self  = {};
62     bless ($self, $class);
63
64     my $spec_files16 = \@{$self->{SPEC_FILES16}};
65     my $spec_files32 = \@{$self->{SPEC_FILES32}};
66     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
67     my $spec_file2dir = \%{$self->{SPEC_FILE2DIR}};
68     my $spec_file2module = \%{$self->{SPEC_FILE2MODULE}};
69     my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
70
71     my $module_file = "$winapi_check_dir/modules.dat";
72
73     if($options->progress) {
74         $output->progress("modules.dat");
75     }
76
77     my %spec_file_found;
78     my $allowed_dir;
79     my $spec_file;
80
81     open(IN, "< $module_file");
82     local $/ = "\n";
83     while(<IN>) {
84         s/^\s*?(.*?)\s*$/$1/; # remove whitespace at begining and end of line
85         s/^(.*?)\s*#.*$/$1/;  # remove comments
86         /^$/ && next;         # skip empty lines
87
88         if(/^%\s+(.*?)$/) {
89             $spec_file = $1;
90
91             if(!-f "$wine_dir/$spec_file") {
92                 $output->write("modules.dat: $spec_file: file ($spec_file) doesn't exist or is no file\n");
93             } 
94
95             $spec_file_found{$spec_file}++;
96             $$spec_file2dir{$spec_file} = {};
97             next;
98         } else {
99             $allowed_dir = $1;
100             $$spec_file2dir{$spec_file}{$allowed_dir}++;
101         }
102         $$dir2spec_file{$allowed_dir}{$spec_file}++;
103
104         if(!-d "$wine_dir/$allowed_dir") {
105             $output->write("modules.dat: $spec_file: directory ($allowed_dir) doesn't exist or is no directory\n");
106         } 
107     }
108     close(IN);
109
110     my @spec_files;
111     if($wine_dir eq ".") {
112         @spec_files = get_spec_files("winelib");
113     } else {
114         my %spec_files = ();
115         foreach my $dir ($options->directories) {
116             $dir = "$current_dir/$dir";
117             $dir =~ s%/\.$%%;
118             foreach my $spec_file (sort(keys(%{$$dir2spec_file{$dir}}))) {
119                 $spec_files{$spec_file}++;
120             }
121         }
122         @spec_files = sort(keys(%spec_files));
123     }
124
125     @$spec_files16 = ();
126     @$spec_files32 = ();
127     foreach my $spec_file (@spec_files) {
128         (my $type, my $module) = get_spec_file_type("$wine_dir/$spec_file");
129
130         $$spec_file2module{$spec_file} = $module;
131         $$module2spec_file{$module} = $spec_file;
132
133         if($type eq "win16") {
134             push @$spec_files16, $spec_file;
135         } elsif($type eq "win32") {
136             push @$spec_files32, $spec_file;
137         } else {
138             $output->write("$spec_file: unknown type '$type'\n");
139         }
140     }
141
142     foreach my $spec_file (@spec_files) {
143         if(!$spec_file_found{$spec_file}) {
144             $output->write("modules.dat: $spec_file: exists but is not specified\n");
145         }
146     }
147
148     $modules = $self;
149
150     return $self;
151 }
152
153 sub all_modules {
154     my $self = shift;
155
156     my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
157
158     return sort(keys(%$module2spec_file));
159 }
160
161 sub complete_modules {
162     my $self = shift;
163
164     my $c_files = shift;
165
166     my %dirs;
167
168     foreach my $file (@$c_files) {
169         my $dir = file_directory("$current_dir/$file");
170         $dirs{$dir}++;
171     }
172
173     my @c_files = get_c_files("winelib");
174     @c_files = files_skip(@c_files);
175     foreach my $file (@c_files) {
176         my $dir = file_directory($file);
177         if(exists($dirs{$dir})) {
178             $dirs{$dir}--;
179         }
180     }
181
182     my @complete_modules = ();
183     foreach my $module ($self->all_modules) {
184         my $index = -1;
185         my @dirs = $self->allowed_dirs_for_module($module);
186         foreach my $dir (@dirs) {
187             if(exists($dirs{$dir}) && $dirs{$dir} == 0) { 
188                 $index++;
189             }
190         }
191         if($index == $#dirs) {
192             push @complete_modules, $module;
193         }
194     }
195
196     return @complete_modules;
197 }
198
199 sub is_allowed_module {
200     my $self = shift;
201
202     my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
203
204     my $module = shift;
205
206     return defined($$module2spec_file{$module});
207 }
208
209 sub is_allowed_module_in_file {
210     my $self = shift;
211
212     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
213     my $spec_file2module = \%{$self->{SPEC_FILE2MODULE}};
214
215     my $module = shift;
216     my $file = shift;
217     $file =~ s/^\.\///;
218
219     my $dir = $file;
220     $dir =~ s/\/[^\/]*$//;
221
222     if($dir =~ m%^include%) {
223         return 1;
224     }
225
226     foreach my $spec_file (sort(keys(%{$$dir2spec_file{$dir}}))) {
227         if($$spec_file2module{$spec_file} eq $module) {
228             return 1;
229         }
230     }
231
232     return 0;
233 }
234
235 sub allowed_modules_in_file {
236     my $self = shift;
237
238     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
239     my $spec_file2module = \%{$self->{SPEC_FILE2MODULE}};
240
241     my $file = shift;
242     $file =~ s/^\.\///;
243
244     my $dir = $file;
245     $dir =~ s/\/[^\/]*$//;
246
247     my %allowed_modules = ();
248     foreach my $spec_file (sort(keys(%{$$dir2spec_file{$dir}}))) {
249         my $module = $$spec_file2module{$spec_file};
250         $allowed_modules{$module}++;
251     }
252
253     my $module = join(" & ", sort(keys(%allowed_modules)));
254
255     return $module;
256 }
257
258 sub allowed_dirs_for_module {
259    my $self = shift;
260
261    my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
262    my $spec_file2dir = \%{$self->{SPEC_FILE2DIR}};   
263
264    my $module = shift;
265
266    my $spec_file = $$module2spec_file{$module};
267
268    return sort(keys(%{$$spec_file2dir{$spec_file}}));
269 }
270
271 sub allowed_spec_files16 {
272     my $self = shift;
273
274     my $spec_files16 = \@{$self->{SPEC_FILES16}};
275
276     return @$spec_files16;
277 }
278
279 sub allowed_spec_files32 {
280     my $self = shift;
281
282     my $spec_files32 = \@{$self->{SPEC_FILES32}};
283
284     return @$spec_files32;
285 }
286
287 sub found_module_in_dir {
288     my $self = shift;
289
290     my $module = shift;
291     my $dir = shift;
292
293     my $used_module_dirs = \%{$self->{USED_MODULE_DIRS}};
294
295     $dir = "$current_dir/$dir";
296     $dir =~ s%/\.$%%;
297
298     $$used_module_dirs{$module}{$dir}++;
299 }
300
301 sub global_report {
302     my $self = shift;
303
304     my $dir2spec_file = \%{$self->{DIR2SPEC_FILE}};
305     my $module2spec_file = \%{$self->{MODULE2SPEC_FILE}};
306     my $used_module_dirs = \%{$self->{USED_MODULE_DIRS}};
307
308     my @messages;
309     foreach my $dir ($options->directories) {
310         $dir = "$current_dir/$dir";
311         $dir =~ s%/\.$%%;
312         foreach my $module ($self->all_modules) {
313             if(!$$used_module_dirs{$module}{$dir}) {
314                 my $spec_file = $$module2spec_file{$module};
315                 push @messages, "modules.dat: $spec_file: directory ($dir) is not used\n";
316             }
317         }
318     }
319
320     foreach my $message (sort(@messages)) {
321         $output->write($message);
322     }
323 }
324
325 1;