Make winelauncher work better for source tree builds.
[wine] / tools / winapi_check / winapi_parser.pm
1 package winapi_parser;
2
3 use strict;
4
5 sub parse_c_file {
6     my $options = shift;
7     my $output = shift;
8     my $file = shift;
9     my $function_found_callback = shift;
10     my $preprocessor_found_callback = shift;
11
12     my $documentation;
13     my $linkage;
14     my $return_type;
15     my $calling_convention;
16     my $function = "";
17     my $argument_types;
18     my $argument_names;
19     my $argument_documentations;
20     my $statements;
21
22     my $function_begin = sub {
23         $documentation = shift;
24         $linkage = shift;
25         $return_type= shift;
26         $calling_convention = shift;
27         $function = shift;
28         $argument_types = shift;
29         $argument_names = shift;
30         $argument_documentations = shift;
31
32         if($#$argument_names == -1) {
33             foreach my $n (0..$#$argument_types) {
34                 push @$argument_names, "";
35             }
36         }
37
38         if($#$argument_documentations == -1) {
39             foreach my $n (0..$#$argument_documentations) {
40                 push @$argument_documentations, "";
41             }
42         }
43
44         $statements = "";
45     };
46     my $function_end = sub {
47         &$function_found_callback($documentation,$linkage,$return_type,
48                                   $calling_convention,$function,$argument_types,
49                                   $argument_names,$argument_documentations,$statements);
50         $function = "";
51     };
52
53     my %regs_entrypoints;
54     my @comments = ();
55     my $level = 0;
56     my $extern_c = 0;
57     my $again = 0;
58     my $lookahead = 0;
59     my $lookahead_count = 0;
60
61     print STDERR "Processing file '$file' ... " if $options->verbose;
62     open(IN, "< $file") || die "<internal>: $file: $!\n";
63     $/ = "\n";
64     while($again || defined(my $line = <IN>)) {
65         if(!$again) {
66             chomp $line;
67
68             if($lookahead) {
69                 $lookahead = 0;
70                 $_ .= "\n" . $line;
71             } else {
72                 $_ = $line;
73                 $lookahead_count = 0;
74             }
75             $lookahead_count++;
76             print " $level($lookahead_count): $line\n" if $options->debug >= 2;
77             print "*** $_\n" if $options->debug >= 3;
78         } else {
79             $lookahead_count = 0;
80             $again = 0;
81         }
82
83         # Merge conflicts in file?
84         if(/^(<<<<<<<|=======|>>>>>>>)/) {
85             $output->write("$file: merge conflicts in file\n");
86             last;
87         }
88       
89         # remove C comments
90         if(s/^(.*?)(\/\*.*?\*\/)(.*)$/$1 $3/s) { push @comments, $2; $again = 1; next }
91         if(/^(.*?)\/\*/s) {
92             $lookahead = 1;
93             next;
94         }
95
96         # remove C++ comments
97         while(s/^(.*?)\/\/.*?$/$1\n/s) { $again = 1 }
98         if($again) { next; }
99
100         # remove empty rows
101         if(/^\s*$/) { next; }
102
103         # remove preprocessor directives
104         if(s/^\s*\#/\#/m) {
105             if(/^\\#.*?\\$/m) {
106                 $lookahead = 1;
107                 next;
108             } elsif(s/^\#\s*(.*?)(\s+(.*?))?\s*$//m) {
109                 if(defined($3)) {
110                     &$preprocessor_found_callback($1, $3);
111                 } else {
112                     &$preprocessor_found_callback($1, "");
113                 }
114                 next;
115             }
116         }
117
118         # Remove extern "C"
119         if(s/^\s*extern\s+"C"\s+\{//m) { 
120             $extern_c = 1;
121             $again = 1;
122             next; 
123         }
124
125         my $documentation;
126         my @argument_documentations;
127         {
128             my $n = $#comments;
129             while($n >= 0 && ($comments[$n] !~ /^\/\*\*/ ||
130                               $comments[$n] =~ /^\/\*\*+\//)) 
131             {
132                 $n--;
133             }
134
135             if(defined($comments[$n]) && $n >= 0) {
136                 $documentation = $comments[$n];
137                 for(my $m=$n+1; $m <= $#comments; $m++) {
138                     push @argument_documentations, $comments[$m];
139                 }
140             } else {
141                 $documentation = "";
142             }
143         }
144
145         if($level > 0)
146         {
147             my $line = "";
148             while(/^[^\{\}]/) {
149                 s/^([^\{\}\'\"]*)//s;
150                 $line .= $1;
151                 if(s/^\'//) {
152                     $line .= "\'";
153                     while(/^./ && !s/^\'//) {
154                         s/^([^\'\\]*)//s;
155                         $line .= $1;
156                         if(s/^\\//) {
157                             $line .= "\\";
158                             if(s/^(.)//s) {
159                                 $line .= $1;
160                                 if($1 eq "0") {
161                                     s/^(\d{0,3})//s;
162                                     $line .= $1;
163                                 }
164                             }
165                         }
166                     }
167                     $line .= "\'";
168                 } elsif(s/^\"//) {
169                     $line .= "\"";
170                     while(/^./ && !s/^\"//) {
171                         s/^([^\"\\]*)//s;
172                         $line .= $1;
173                         if(s/^\\//) {
174                             $line .= "\\";
175                             if(s/^(.)//s) {
176                                 $line .= $1;
177                                 if($1 eq "0") {
178                                     s/^(\d{0,3})//s;
179                                     $line .= $1;
180                                 }
181                             }
182                         }
183                     }
184                     $line .= "\"";
185                 }
186             }
187
188             if(s/^\{//) {
189                 $_ = $'; $again = 1;
190                 $line .= "{";
191                 print "+1: \{$_\n" if $options->debug >= 2;
192                 $level++;
193             } elsif(s/^\}//) {
194                 $_ = $'; $again = 1;
195                 $line .= "}" if $level > 1;
196                 print "-1: \}$_\n" if $options->debug >= 2; 
197                 $level--;
198                 if($level == -1 && $extern_c) {
199                     $extern_c = 0;
200                     $level = 0;
201                 }
202             }
203
204             if($line !~ /^\s*$/) {
205                 $statements .= "$line\n";
206             }
207
208             if($function && $level == 0) {
209                 &$function_end;
210             }
211             next;           
212         } elsif(/(extern\s+|static\s+)?((struct\s+|union\s+|enum\s+)?\w+((\s*\*)+\s*|\s+))
213             ((__cdecl|__stdcall|CDECL|VFWAPIV|VFWAPI|WINAPIV|WINAPI|CALLBACK)\s+)?
214             (\w+(\(\w+\))?)\s*\(([^\)]*)\)\s*(\{|\;)/sx)
215         {
216             $_ = $'; $again = 1;
217             
218             if($11 eq "{")  {
219                 $level++;
220             }
221
222             my $linkage = $1;
223             my $return_type = $2;
224             my $calling_convention = $7;
225             my $name = $8;
226             my $arguments = $10;
227
228             if(!defined($linkage)) {
229                 $linkage = "";
230             }
231
232             if(!defined($calling_convention)) {
233                 $calling_convention = "";
234             }
235
236             $linkage =~ s/\s*$//;
237
238             $return_type =~ s/\s*$//;
239             $return_type =~ s/\s*\*\s*/*/g;
240             $return_type =~ s/(\*+)/ $1/g;
241
242             if($regs_entrypoints{$name}) {
243                 $name = $regs_entrypoints{$name};
244             } 
245
246             $arguments =~ y/\t\n/  /;
247             $arguments =~ s/^\s*(.*?)\s*$/$1/;
248             if($arguments eq "") { $arguments = "void" }
249
250             my @argument_types;
251             my @argument_names;
252             my @arguments = split(/,/, $arguments);
253             foreach my $n (0..$#arguments) {
254                 my $argument_type = "";
255                 my $argument_name = "";
256                 my $argument = $arguments[$n];
257                 $argument =~ s/^\s*(.*?)\s*$/$1/;
258                 # print "  " . ($n + 1) . ": '$argument'\n";
259                 $argument =~ s/^(IN OUT(?=\s)|IN(?=\s)|OUT(?=\s)|\s*)\s*//;
260                 $argument =~ s/^(const(?=\s)|CONST(?=\s)|\s*)\s*//;
261                 if($argument =~ /^\.\.\.$/) {
262                     $argument_type = "...";
263                     $argument_name = "...";
264                 } elsif($argument =~ /^
265                         ((?:struct\s+|union\s+|enum\s+|(?:signed\s+|unsigned\s+)
266                           (?:short\s+(?=int)|long\s+(?=int))?)?\w+)\s*
267                         ((?:const)?\s*(?:\*\s*?)*)\s*
268                         (?:WINE_UNUSED\s+)?(\w*)\s*(?:\[\]|\s+OPTIONAL)?/x)
269                 {
270                     $argument_type = "$1";
271                     if($2 ne "") {
272                         $argument_type .= " $2";
273                     }
274                     $argument_name = $3;
275
276                     $argument_type =~ s/\s*const\s*/ /;
277                     $argument_type =~ s/^\s*(.*?)\s*$/$1/;
278
279                     $argument_name =~ s/^\s*(.*?)\s*$/$1/;
280                 } else {
281                     die "$file: $.: syntax error: '$argument'\n";
282                 }
283                 $argument_types[$n] = $argument_type;
284                 $argument_names[$n] = $argument_name;
285                 # print "  " . ($n + 1) . ": '" . $argument_types[$n] . "', '" . $argument_names[$n] . "'\n";
286             }
287             if($#argument_types == 0 && $argument_types[0] =~ /^void$/i) {
288                 $#argument_types = -1;
289                 $#argument_names = -1;  
290             }
291
292             if($options->debug) {
293                 print "$file: $return_type $calling_convention $name(" . join(",", @arguments) . ")\n";
294             }
295             
296             &$function_begin($documentation,$linkage,$return_type,$calling_convention,$name,\@argument_types,\@argument_names,\@argument_documentations);
297             if($level == 0) {
298                 &$function_end;
299             }
300         } elsif(/DC_(GET_X_Y|GET_VAL_16)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
301             $_ = $'; $again = 1;
302             my @arguments = ("HDC16");
303             &$function_begin($documentation, "", $2, "WINAPI", $3, \@arguments);
304             &$function_end;
305         } elsif(/DC_(GET_VAL)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,.*?\)/s) {
306             $_ = $'; $again = 1;
307             my $return16 = $3 . "16";
308             my $return32 = $3;
309             my $name16 = $2 . "16";
310             my $name32 = $2;
311             my @arguments16 = ("HDC16");
312             my @arguments32 = ("HDC");
313
314             if($name16 eq "COLORREF16") { $name16 = "COLORREF"; }
315
316             &$function_begin($documentation, "", $name16, "WINAPI", $return16, \@arguments16);
317             &$function_end;
318             &$function_begin($documentation, "", $name32, "WINAPI", $return32, \@arguments32);
319             &$function_end;
320         } elsif(/DC_(GET_VAL_EX)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
321             $_ = $'; $again = 1;
322             my @arguments16 = ("HDC16", "LP" . $5 . "16");
323             my @arguments32 = ("HDC", "LP" . $5);
324             &$function_begin($documentation, "", "BOOL16", "WINAPI", $2 . "16", \@arguments16);
325             &$function_end;
326             &$function_begin($documentation, "", "BOOL", "WINAPI", $2, \@arguments32);
327             &$function_end;
328         } elsif(/DC_(SET_MODE)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
329             $_ = $'; $again = 1;
330             my @arguments16 = ("HDC16", "INT16");
331             my @arguments32 = ("HDC", "INT");
332             &$function_begin($documentation, "", "INT16", "WINAPI", $2 . "16", \@arguments16);
333             &$function_end;
334             &$function_begin($documentation, "", "INT", "WINAPI", $2, \@arguments32);
335             &$function_end;
336         } elsif(/WAVEIN_SHORTCUT_0\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
337             $_ = $'; $again = 1;
338             my @arguments16 = ("HWAVEIN16");
339             my @arguments32 = ("HWAVEIN");
340             &$function_begin($documentation, "", "UINT16", "WINAPI", "waveIn" . $1 . "16", \@arguments16);
341             &$function_end;
342             &$function_begin($documentation, "", "UINT", "WINAPI", "waveIn" . $1, \@arguments32);
343             &$function_end;         
344         } elsif(/WAVEOUT_SHORTCUT_0\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
345             $_ = $'; $again = 1;
346             my @arguments16 = ("HWAVEOUT16");
347             my @arguments32 = ("HWAVEOUT");
348             &$function_begin($documentation, "", "UINT16", "WINAPI", "waveOut" . $1 . "16", \@arguments16);
349             &$function_end;
350             &$function_begin($documentation, "", "UINT", "WINAPI", "waveOut" . $1, \@arguments32);          
351             &$function_end;
352         } elsif(/WAVEOUT_SHORTCUT_(1|2)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
353             $_ = $'; $again = 1;
354             if($1 eq "1") {
355                 my @arguments16 = ("HWAVEOUT16", $4);
356                 my @arguments32 = ("HWAVEOUT", $4);
357                 &$function_begin($documentation, "", "UINT16", "WINAPI", "waveOut" . $2 . "16", \@arguments16);
358                 &$function_end;
359                 &$function_begin($documentation, "", "UINT", "WINAPI", "waveOut" . $2, \@arguments32);
360                 &$function_end;
361             } elsif($1 eq 2) {
362                 my @arguments16 = ("UINT16", $4);
363                 my @arguments32 = ("UINT", $4);
364                 &$function_begin($documentation, "", "UINT16", "WINAPI", "waveOut". $2 . "16", \@arguments16);
365                 &$function_end;
366                 &$function_begin($documentation, "", "UINT", "WINAPI", "waveOut" . $2, \@arguments32);
367                 &$function_end;
368             }
369         } elsif(/DEFINE_REGS_ENTRYPOINT_\d+\(\s*(\S*)\s*,\s*([^\s,\)]*).*?\)/s) {
370             $_ = $'; $again = 1;
371             $regs_entrypoints{$2} = $1;
372         } elsif(/\'[^\']*\'/s) {
373             $_ = $'; $again = 1;
374         } elsif(/\"[^\"]*\"/s) {
375             $_ = $'; $again = 1;
376         } elsif(/;/s) {
377             $_ = $'; $again = 1;
378         } elsif(/extern\s+"C"\s+{/s) {
379             $_ = $'; $again = 1;
380         } elsif(/\{/s) {
381             $_ = $'; $again = 1;
382             print "+1: $_\n" if $options->debug >= 2;
383             $level++;
384         } else {
385             $lookahead = 1;
386         }
387     }
388     close(IN);
389     print STDERR "done\n" if $options->verbose;
390     $output->write("$file: not at toplevel at end of file\n") unless $level == 0;
391 }
392
393 1;