- Begun implementation of a C statements parser.
[wine] / tools / winapi / winapi_fixup_documentation.pm
1 package winapi_fixup_documentation;
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(&fixup_documentation);
11
12 use config qw($current_dir $wine_dir);
13 use modules qw($modules);
14 use options qw($options);
15 use output qw($output);
16 use winapi qw($win16api $win32api @winapis);
17
18 my %documentation_line_used;
19
20 sub fixup_documentation {
21     my $function = shift;
22     my $editor = shift;
23
24     my $file = $function->file;
25     my $documentation_line = $function->documentation_line;
26     my $documentation = $function->documentation;
27     my $function_line = $function->function_line;
28     my $linkage = $function->linkage;
29     my $return_type = $function->return_type;
30     my $calling_convention = $function->calling_convention;
31     my $internal_name = $function->internal_name;
32     my $statements = $function->statements;
33     
34     if($linkage eq "static" ||
35        ($linkage eq "extern" && !defined($statements)) ||
36        ($linkage eq "" && !defined($statements)))
37     {
38         return;
39     }
40     
41     my @external_names = $function->external_names;
42     if($#external_names < 0) {
43         return;
44     }
45
46     if($documentation_line_used{$file}{documentation_line}) {
47         $documentation = undef;
48     }
49     $documentation_line_used{$file}{$documentation_line}++;
50     
51     my @module_ordinal_entries = ();
52     foreach my $entry2 ($function->get_all_module_ordinal) {
53         (my $external_name2, my $module2, my $ordinal2) = @$entry2;
54         if(($external_name2 eq "@" ||
55             ($win16api->is_module($module2) && !$win16api->is_function_stub_in_module($module2, $external_name2)) ||
56             ($win32api->is_module($module2) && !$win32api->is_function_stub_in_module($module2, $external_name2))) &&
57                $modules->is_allowed_module_in_file($module2, "$current_dir/$file"))
58         {
59             push @module_ordinal_entries, $entry2; 
60         }
61     }
62     
63     my $spec_modified = 0;
64     
65     if($options->stub && defined($documentation)) {
66         my $calling_convention16 = $function->calling_convention16;
67         my $calling_convention32 = $function->calling_convention32;
68         
69         foreach my $winapi (@winapis) {
70             my @entries = ();
71             my $module = $winapi->function_internal_module($internal_name);
72             my $ordinal = $winapi->function_internal_ordinal($internal_name);
73             
74             if($winapi->is_function_stub_in_module($module, $internal_name)) {
75                 my $external_name = $internal_name;
76                 if($winapi->name eq "win16") {
77                     $external_name =~ s/(?:_)?16([AW]?)$//;
78                     if(defined($1)) {
79                         $external_name .= $1;
80                     }
81                 }
82                 push @entries, [$external_name, $module, $ordinal];
83             }
84             
85             foreach (split(/\n/, $documentation)) {
86                 if(/^\s*\*\s*(\S+)\s*[\(\[]\s*(\w+)\s*\.\s*([^\s\)\]]*)\s*[\)\]].*?$/) {
87                     my $external_name = $1;
88                     my $module = lc($2);
89                     my $ordinal = $3;
90                     
91                     if($external_name ne "@" && 
92                        $winapi->is_module($module) &&
93                        $winapi->is_function_stub_in_module($module, $external_name) &&
94                        $internal_name !~ /^\U$module\E_\Q$external_name\E$/)
95                     {
96                         push @entries, [$external_name, $module, $ordinal];
97                     }
98                 }
99             }
100             
101             foreach my $entry (@entries) {
102                 (my $external_name, my $module, my $ordinal) = @$entry;
103                 
104                 my $refargument_types = $function->argument_types;
105                 
106                 if(!defined($refargument_types)) {
107                     next;
108                 }
109                 
110                 my $abort = 0;
111                 my $n;
112                 my @argument_kinds = map {
113                     my $type = $_;
114                     my $kind;
115                     if($type ne "..." && !defined($kind = $winapi->translate_argument($type))) {
116                         $output->write("no translation defined: " . $type . "\n");
117                     }
118                     
119                     # FIXME: Kludge
120                     if(defined($kind) && $kind eq "longlong") {
121                         $n += 2;
122                         ("long", "long");
123                     } elsif(defined($kind)) {
124                         $n++;
125                         $kind;
126                     } elsif($type eq "...") {
127                         if($winapi->name eq "win16") {
128                             $calling_convention16 = "pascal"; # FIXME: Is this correct?
129                         } else {
130                             $calling_convention32 = "varargs";
131                         }
132                         ();
133                     } else {
134                         $abort = 1;
135                         $n++;
136                         "undef";
137                     }
138                 } @$refargument_types;
139                 
140                 my $search = "^\\s*$ordinal\\s+stub\\s+$external_name\\s*(?:#.*?)?\$";
141                 my $replace;
142                 if($winapi->name eq "win16") {
143                     $replace = "$ordinal $calling_convention16 $external_name(@argument_kinds) $internal_name";
144                 } else {
145                     $replace = "$ordinal $calling_convention32 $external_name(@argument_kinds) $internal_name";
146                 }
147
148                 if(!$abort) {
149                     $spec_modified = 1;
150                     $editor->replace_spec_file($module, $search, $replace);
151                 }
152             }       
153         }
154     }
155     
156     my %found_external_names;
157     foreach my $external_name (@external_names) {
158         $found_external_names{$external_name} = {};
159     }
160     
161     my $documentation_modified = 0;
162     
163     if(!$spec_modified &&
164        (defined($documentation) && !$documentation_modified) &&
165        ($options->documentation_name || $options->documentation_ordinal || 
166         $options->documentation_missing))
167     {
168         local $_;
169         
170         my $line3;
171         my $search;
172         my $replace;
173         
174         my $count = 0;
175         my $line2 = $documentation_line - 1;
176         foreach (split(/\n/, $documentation)) {
177             $line2++;
178             if(/^(\s*\*\s*(\S+)\s*)((?:\s*[\(\[]\s*\w+(?:\s*\.\s*[^\s\)\]]*\s*)?[\)\]])+)(.*?)$/) {
179                 my $part1 = $1;
180                 my $external_name = $2;
181                 my $part3 = $3;
182                 my $part4 = $4;
183                 
184                 $part4 =~ s/\s*$//;
185                 
186                 my @entries = ();
187                 while($part3 =~ s/^\s*([\(\[]\s*(\w+)(?:\s*\.\s*([^\s\)\]]*)\s*)?[\)\]])//) {
188                     push @entries, [$1, $2, $3];
189                 }
190                 
191                 my $found = 0;
192                 foreach my $external_name2 (@external_names) {
193                     if($external_name eq $external_name2) {
194                         foreach my $entry (@entries) {
195                             (undef, my $module, undef) = @$entry;
196                             $found_external_names{$external_name2}{$module} = 1;
197                         }
198                         $found = 1;
199                         last;
200                         }
201                 }
202                 
203                 my $replaced = 0;
204                 my $replace2 = "";
205                 foreach my $entry (@entries) {
206                     my $part12 = $part1;
207                     (my $part32, my $module, my $ordinal) = @$entry;
208                     
209                     foreach my $entry2 (@module_ordinal_entries) {
210                         (my $external_name2, my $module2, my $ordinal2) = @$entry2;
211                         
212                         if($options->documentation_name && lc($module) eq $module2 && 
213                            $external_name ne $external_name2) 
214                         {
215                             if(!$found && $part12 =~ s/\b\Q$external_name\E\b/$external_name2/) {
216                                 $external_name = $external_name2;
217                                 $replaced++;
218                             }
219                         }
220                         
221                         if($options->documentation_ordinal &&
222                            $external_name eq $external_name2 &&
223                            lc($module) eq $module2 && 
224                            ($#entries > 0 || !defined($ordinal) || ($ordinal ne $ordinal2)))
225                         {
226                             if(defined($ordinal)) {
227                                 if($part32 =~ s/\Q$module\E\s*.\s*\Q$ordinal\E/\U$module2\E.$ordinal2/ || $#entries > 0) {
228                                     $replaced++;
229                                 }
230                             } else {
231                                 if($part32 =~ s/\Q$module\E/\U$module2\E.$ordinal2/ || $#entries > 0) {
232                                     $replaced++;
233                                 }
234                             }
235                         }
236                     }
237                     if($replace2) { $replace2 .= "\n"; }
238                     $replace2 .= "$part12$part32$part4";
239                 }
240                 
241                 if($replaced > 0) {
242                     $line3 = $line2;
243                     $search = "^\Q$_\E\$";
244                     $replace = $replace2;
245                 }
246                 $count++;
247             } elsif(/^(\s*\*\s*)([^\s\(]+)(?:\(\))?\s*$/) {
248                 my $part1 = $1;
249                 my $external_name = $2;
250
251                 if($internal_name =~ /^(?:\S+_)?\Q$external_name\E(?:16)?$/) {
252                     foreach my $entry (@module_ordinal_entries) {
253                         (my $external_name2, my $module, my $ordinal) = @$entry;
254
255                         $line3 = $line2;
256                         $search = "^\Q$_\E\$";
257                         $replace = "$part1$external_name2 (\U$module\E.$ordinal)";
258                     }
259                     $count++;
260                 }
261             }
262         }
263
264         if(defined($line3) && defined($search) && defined($replace)) {
265             if($count > 1 || $#external_names >= 1) {
266                 $output->write("multiple entries (fixup not supported)\n");
267                 # $output->write("s/$search/$replace/\n");
268                 # $output->write("@external_names\n");
269             } else {
270                 $documentation_modified = 1;
271                 $editor->substitute_line($line3, $search, $replace);
272             }
273         }
274     }
275
276     if(!$spec_modified && !$documentation_modified &&
277        $options->documentation_missing && defined($documentation))
278     {
279         my $part1;
280         my $part2;
281         my $part3;
282         my $part4;
283         my $line3 = 0;
284         
285         my $line2 = $documentation_line - 1;
286         foreach (split(/\n/, $documentation)) {
287             $line2++;
288             if(/^(\s*\*\s*)(\S+\s*)([\(\[])\s*\w+\s*\.\s*[^\s\)\]]*\s*([\)\]]).*?$/) {
289                 $part1 = $1;
290                 $part2 = $2;
291                 $part3 = $3;
292                 $part4 = $4;
293
294                 $part2 =~ s/\S/ /g;
295                 
296                 $line3 = $line2 + 1;
297             }
298         }
299
300         foreach my $entry2 (@module_ordinal_entries) {
301             (my $external_name2, my $module2, my $ordinal2) = @$entry2;
302             
303             my $found = 0;
304             foreach my $external_name (keys(%found_external_names)) {
305                 foreach my $module3 (keys(%{$found_external_names{$external_name}})) {
306                     if($external_name eq $external_name2 && uc($module2) eq $module3) {
307                         $found = 1;
308                     }
309                 }
310             }
311             # FIXME: Not 100% correct
312             if(!$found && 
313                !$win16api->is_function_stub_in_module($module2, $internal_name) && 
314                !$win32api->is_function_stub_in_module($module2, $internal_name)) 
315             {
316                 if($line3 > 0) {
317                     $documentation_modified = 1;
318                     $part2 = $external_name2 . " " x (length($part2) - length($external_name2));
319                     $editor->insert_line($line3, "$part1$part2$part3\U$module2\E.$ordinal2$part4\n");
320                 } else {
321                     $output->write("$external_name2 (\U$module2\E.$ordinal2) missing (fixup not supported)\n");
322                 }
323             }
324         }
325     }
326
327     if(!$documentation_modified && 
328        defined($documentation) &&
329        $options->documentation_wrong)
330     {
331         my $line2 = $documentation_line - 1;
332         foreach (split(/\n/, $documentation)) {
333             $line2++;
334             if(/^\s*\*\s*(\S+)\s*[\(\[]\s*(\w+)\s*\.\s*([^\s\)\]]*)\s*[\)\]].*?$/) {
335                 my $external_name = $1;
336                 my $module = $2;
337                 my $ordinal = $3;
338                 
339                 my $found = 0;
340                 foreach my $entry2 (@module_ordinal_entries) {
341                     (my $external_name2, my $module2, my $ordinal2) = @$entry2;
342                     
343                     if($external_name eq $external_name2 &&
344                        lc($module) eq $module2 &&
345                        $ordinal eq $ordinal2) 
346                     {
347                         $found = 1;
348                     }
349                 }
350                 if(!$found) {
351                     if(1) {
352                         $documentation_modified = 1;
353
354                         $editor->delete_line($line2, "^\Q$_\E\$");
355                     } else {
356                         $output->write("$external_name (\U$module\E.$ordinal) wrong (fixup not supported)\n");
357                     };
358                 }
359             }
360         }
361     }
362
363     if(!$spec_modified && !$documentation_modified && !defined($documentation))
364     {
365         my $insert = "";
366         foreach my $winapi (@winapis) {
367             my $external_name = $winapi->function_external_name($internal_name);
368             my $module = $winapi->function_internal_module($internal_name);
369             my $ordinal = $winapi->function_internal_ordinal($internal_name);
370
371             if(defined($external_name) && defined($module) && defined($ordinal)) {
372                 $insert .= " *\t\t$external_name (\U$module\E.$ordinal)\n";
373             }
374         }
375         if($insert) {
376             $editor->insert_line($function_line,
377                                  "/" . "*" x 71 . "\n" .
378                                  "$insert" .
379                                  " */\n");
380         }
381     }
382 }
383
384 1;