- FreeBSD now supported.
[wine] / tools / winapi / winapi_extract
1 #!/usr/bin/perl -w
2
3 # Copyright 2001 Patrik Stridvall
4
5 use strict;
6
7 BEGIN {
8     $0 =~ m%^(.*?/?tools)/winapi/winapi_extract$%;
9     require "$1/winapi/setup.pm";
10 }
11
12 use config qw(
13     &file_type &files_skip &files_filter &get_spec_files
14     $current_dir $wine_dir $winapi_dir $winapi_check_dir
15 );
16 use output qw($output);
17 use winapi_extract_options qw($options);
18
19 if($options->progress) {
20     $output->enable_progress;
21 } else {
22     $output->disable_progress;
23 }
24
25 use function;
26 use type;
27 use winapi_function;
28 use winapi_parser;
29 use winapi qw(@winapis);
30
31 my %module2spec_file;
32 my %module2type;
33 if($options->spec_files) {
34     local $_;
35
36     foreach my $spec_file (get_spec_files("winelib")) {
37         my $module;
38         my $type;
39
40         open(IN, "< $wine_dir/$spec_file");
41         while(<IN>) {
42             s/^\s*?(.*?)\s*$/$1/; # remove whitespace at begining and end of line
43             s/^(.*?)\s*#.*$/$1/;  # remove comments
44             /^$/ && next;         # skip empty lines
45
46             if(/^name\s+(.*?)$/) {
47                 $module = $1;
48                 $module2spec_file{$module} = $spec_file;
49             } elsif(/^type\s+(.*?)$/) {
50                 $type = $1;
51                 $module2type{$module} = $type;
52             }
53         }
54         close(IN);
55     }
56 }
57
58 my %specifications;
59
60 sub documentation_specifications {
61     my $function = shift;
62
63     my @debug_channels = @{$function->debug_channels};
64     my $documentation = $function->documentation;
65     my $documentation_line = $function->documentation_line;
66     my $return_type = $function->return_type;
67     my $linkage = $function->linkage;
68     my $internal_name = $function->internal_name;
69
70     if($linkage eq "static") {
71         return;
72     }
73
74     local $_;
75     foreach (split(/\n/, $documentation)) {
76         if(/^\s*\*\s*(\S+)\s*[\(\[]\s*(\w+)\s*\.\s*(\S+)\s*[\)\]]/) {
77             my $external_name = $1;
78             my $module = lc($2);
79             my $ordinal = $3;
80
81             if($ordinal eq "@") {
82                 if(1 || !exists($specifications{$module}{unfixed}{$external_name})) {
83                     $specifications{$module}{unfixed}{$external_name}{ordinal} = $ordinal;
84                     $specifications{$module}{unfixed}{$external_name}{external_name} = $external_name;
85                     $specifications{$module}{unfixed}{$external_name}{function} = $function;
86                 } else {
87                     $output->write("$external_name ($module.$ordinal) already exists\n");
88                 }
89             } elsif($ordinal =~ /^\d+$/) {
90                 if(1 || !exists($specifications{$module}{fixed}{$ordinal})) {
91                     $specifications{$module}{fixed}{$ordinal}{ordinal} = $ordinal;
92                     $specifications{$module}{fixed}{$ordinal}{external_name} = $external_name;
93                     $specifications{$module}{fixed}{$ordinal}{function} = $function;
94                     } else {
95                         $output->write("$external_name ($module.$ordinal) already exists\n");
96                     }
97             } elsif($ordinal eq "init") {
98                 if(!exists($specifications{$module}{init})) {
99                     $specifications{$module}{init}{function} = $function;
100                 } else {
101                     $output->write("$external_name ($module.$ordinal) already exists\n");
102                 }
103             } else {
104                 if(!exists($specifications{$module}{unknown}{$external_name})) {
105                     $specifications{$module}{unknown}{$external_name}{ordinal} = $ordinal;
106                     $specifications{$module}{unknown}{$external_name}{external_name} = $external_name;
107                     $specifications{$module}{unknown}{$external_name}{function} = $function;
108                 } else {
109                     $output->write("$external_name ($module.$ordinal) already exists\n");
110                 }
111             }
112             
113             if($options->debug) {
114                 $output->write("$external_name ($module.$ordinal)\n");
115             }
116         }
117     }
118 }
119
120 my %module_pseudo_stub_count16;
121 my %module_pseudo_stub_count32;
122
123 sub statements_stub {
124     my $function = shift;
125
126     my $statements = $function->statements;
127     if(defined($statements) && $statements =~ /FIXME[^;]*stub/s) {
128         if($options->win16) {
129             foreach my $module16 ($function->modules16) {
130                 $module_pseudo_stub_count16{$module16}++;
131             }
132         }
133         if($options->win32) {
134             foreach my $module32 ($function->modules32) {
135                 $module_pseudo_stub_count32{$module32}++;
136             }
137         }
138     }
139 }
140
141 my @c_files = $options->c_files;
142 @c_files = files_skip(@c_files);
143 @c_files = files_filter("winelib", @c_files);
144
145 my $progress_output;
146 my $progress_current = 0;
147 my $progress_max = scalar(@c_files);
148
149 foreach my $file (@c_files) {
150     my %functions;
151
152     $progress_current++;
153     $output->progress("$file (file $progress_current of $progress_max)");
154
155     my $create_function = sub {
156         if($options->stub_statistics) {
157             return 'winapi_function'->new;
158         } else {
159             return 'function'->new;
160         }
161     };
162
163     my $found_function = sub {
164         my $function = shift;
165
166         my $internal_name = $function->internal_name;
167         $functions{$internal_name} = $function;
168         
169         $output->progress("$file (file $progress_current of $progress_max): $internal_name");
170         $output->prefix_callback(sub { return $function->prefix; });
171
172         my $documentation_line = $function->documentation_line;
173         my $documentation = $function->documentation;
174         my $function_line = $function->function_line;
175         my $linkage = $function->linkage;
176         my $return_type = $function->return_type;
177         my $calling_convention = $function->calling_convention;
178         my $statements = $function->statements;
179
180         if($options->spec_files) {
181             documentation_specifications($function);
182         }
183
184         if($options->stub_statistics) {
185             statements_stub($function);
186         }
187
188         $output->prefix("");
189     };
190
191     my $create_type = sub {
192         return 'type'->new;
193     };
194
195     my $found_type = sub {
196         my $type = shift;
197     };
198
199     my $found_preprocessor = sub {
200         my $directive = shift;
201         my $argument = shift;
202     };
203
204     &winapi_parser::parse_c_file($file, $create_function, $found_function, $create_type, $found_type, $found_preprocessor);
205
206     my @internal_names = keys(%functions);
207     if($#internal_names < 0) {
208         $output->write("$file: doesn't contain any functions\n");
209     }
210 }
211
212 sub output_function {
213     local *OUT = shift;
214     my $type = shift;
215     my $ordinal = shift;
216     my $external_name = shift;
217     my $function = shift;
218
219     my $internal_name = $function->internal_name;
220
221     my $return_kind;
222     my $calling_convention;
223     my $refargument_kinds;
224     if($type eq "win16") {
225         $return_kind = $function->return_kind16 || "undef";
226         $calling_convention = $function->calling_convention16 || "undef";
227         $refargument_kinds = $function->argument_kinds16;
228     } elsif($type eq "win32") {
229         $return_kind = $function->return_kind32 || "undef";
230         $calling_convention = $function->calling_convention32 || "undef";
231         $refargument_kinds = $function->argument_kinds32;
232     }
233
234     if(defined($refargument_kinds)) {
235         my @argument_kinds = map { $_ || "undef"; } @$refargument_kinds;
236         print OUT "$ordinal $calling_convention $external_name(@argument_kinds) $internal_name\n";
237     } else {
238         print OUT "$ordinal $calling_convention $external_name() $internal_name # FIXME: arguments undefined\n";
239     }
240 }
241
242 if($options->spec_files) {
243     foreach my $module (keys(%specifications)) {
244         my $spec_file = $module2spec_file{$module};
245         my $type = $module2type{$module};
246         
247         if(!defined($spec_file) || !defined($type)) {
248             $output->write("$module: doesn't exist\n");
249             next;
250         }
251         
252         $spec_file .= "2";
253         
254         $output->progress("$spec_file");
255         open(OUT, "> $wine_dir/$spec_file");
256
257         print OUT "name $module\n";
258         print OUT "type $type\n";
259         if(exists($specifications{$module}{init})) {
260             my $function = $specifications{$module}{init}{function};
261             print OUT "init " . $function->internal_name . "\n";
262         }
263         print OUT "\n";
264         
265         my %debug_channels;
266         if(exists($specifications{$module}{init})) {
267             my $function = $specifications{$module}{init}{function};
268             foreach my $debug_channel (@{$function->debug_channels}) {
269                 $debug_channels{$debug_channel}++;
270             }
271         }
272         foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
273             my $function = $specifications{$module}{fixed}{$ordinal}{function};
274             foreach my $debug_channel (@{$function->debug_channels}) {
275                 $debug_channels{$debug_channel}++;
276             }
277         }
278         foreach my $name (sort(keys(%{$specifications{$module}{unfixed}}))) {
279             my $function = $specifications{$module}{unfixed}{$name}{function}; 
280             foreach my $debug_channel (@{$function->debug_channels}) {
281                 $debug_channels{$debug_channel}++;
282             }
283         }
284         foreach my $name (sort(keys(%{$specifications{$module}{unknown}}))) {
285             my $function = $specifications{$module}{unknown}{$name}{function};
286             foreach my $debug_channel (@{$function->debug_channels}) {
287                 $debug_channels{$debug_channel}++;
288             }
289         }
290
291         my @debug_channels = sort(keys(%debug_channels));
292         if($#debug_channels >= 0) { 
293             print OUT "debug_channels (" .  join(" ", @debug_channels) . ")\n";
294             print OUT "\n";
295         }
296         
297         my $empty = 1;
298
299         if(!$empty) {
300             print OUT "\n";
301             $empty = 1;
302         }
303         foreach my $external_name (sort(keys(%{$specifications{$module}{unknown}}))) {
304             my $entry = $specifications{$module}{unknown}{$external_name};
305             my $ordinal = $entry->{ordinal}; 
306             my $function = $entry->{function}; 
307             print OUT "# ";
308             output_function(\*OUT, $type, $ordinal, $external_name, $function);
309             $empty = 0;
310         }
311
312         if(!$empty) {
313             print OUT "\n";
314             $empty = 1;
315         }
316         foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
317             my $entry = $specifications{$module}{fixed}{$ordinal};
318             my $external_name = $entry->{external_name}; 
319             my $function = $entry->{function}; 
320             output_function(\*OUT, $type, $ordinal, $external_name, $function);
321             $empty = 0;
322         }
323
324         if(!$empty) {
325             print OUT "\n";
326             $empty = 1;
327         }
328         foreach my $external_name (sort(keys(%{$specifications{$module}{unfixed}}))) {
329             my $entry = $specifications{$module}{unfixed}{$external_name};
330             my $ordinal = $entry->{ordinal};
331             my $function = $entry->{function};
332             output_function(\*OUT, $type, $ordinal, $external_name, $function);
333             $empty = 0;
334         }
335
336         close(OUT);
337     }
338 }
339
340 if($options->stub_statistics) {
341     foreach my $winapi (@winapis) {
342         if($winapi->name eq "win16" && !$options->win16) { next; }
343         if($winapi->name eq "win32" && !$options->win32) { next; }
344
345         my %module_stub_count;
346         my %module_total_count;
347         
348         foreach my $internal_name ($winapi->all_internal_functions,$winapi->all_functions_stub) {
349             foreach my $module (split(/ \& /, $winapi->function_internal_module($internal_name))) {
350                 if($winapi->is_function_stub_in_module($module, $internal_name)) {
351                     $module_stub_count{$module}++;
352                 }
353                 $module_total_count{$module}++;
354             }
355         }
356
357         foreach my $module ($winapi->all_modules) {
358             my $pseudo_stubs;
359             if($winapi->name eq "win16") {
360                 $pseudo_stubs = $module_pseudo_stub_count16{$module};
361             } elsif($winapi->name eq "win32") {
362                 $pseudo_stubs = $module_pseudo_stub_count32{$module};
363             }
364
365             my $real_stubs = $module_stub_count{$module};
366             my $total = $module_total_count{$module};
367
368             if(!defined($real_stubs)) { $real_stubs = 0; }
369             if(!defined($pseudo_stubs)) { $pseudo_stubs = 0; }
370             if(!defined($total)) { $total = 0;}
371
372             my $stubs = $real_stubs + $pseudo_stubs;
373     
374             $output->write("*.c: $module: ");
375             $output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo)\n");
376         }
377     } 
378 }
379