Use libwine and libwine_unicode directly from their build directory
[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                     if($comments[$m] =~ /^\/\*\*+\/$/) {
139                         @argument_documentations = ();
140                         next;
141                     }
142                     push @argument_documentations, $comments[$m];
143                 }
144             } else {
145                 $documentation = "";
146             }
147         }
148
149         if($level > 0)
150         {
151             my $line = "";
152             while(/^[^\{\}]/) {
153                 s/^([^\{\}\'\"]*)//s;
154                 $line .= $1;
155                 if(s/^\'//) {
156                     $line .= "\'";
157                     while(/^./ && !s/^\'//) {
158                         s/^([^\'\\]*)//s;
159                         $line .= $1;
160                         if(s/^\\//) {
161                             $line .= "\\";
162                             if(s/^(.)//s) {
163                                 $line .= $1;
164                                 if($1 eq "0") {
165                                     s/^(\d{0,3})//s;
166                                     $line .= $1;
167                                 }
168                             }
169                         }
170                     }
171                     $line .= "\'";
172                 } elsif(s/^\"//) {
173                     $line .= "\"";
174                     while(/^./ && !s/^\"//) {
175                         s/^([^\"\\]*)//s;
176                         $line .= $1;
177                         if(s/^\\//) {
178                             $line .= "\\";
179                             if(s/^(.)//s) {
180                                 $line .= $1;
181                                 if($1 eq "0") {
182                                     s/^(\d{0,3})//s;
183                                     $line .= $1;
184                                 }
185                             }
186                         }
187                     }
188                     $line .= "\"";
189                 }
190             }
191
192             if(s/^\{//) {
193                 $_ = $'; $again = 1;
194                 $line .= "{";
195                 print "+1: \{$_\n" if $options->debug >= 2;
196                 $level++;
197             } elsif(s/^\}//) {
198                 $_ = $'; $again = 1;
199                 $line .= "}" if $level > 1;
200                 print "-1: \}$_\n" if $options->debug >= 2; 
201                 $level--;
202                 if($level == -1 && $extern_c) {
203                     $extern_c = 0;
204                     $level = 0;
205                 }
206             }
207
208             if($line !~ /^\s*$/) {
209                 $statements .= "$line\n";
210             }
211
212             if($function && $level == 0) {
213                 &$function_end;
214             }
215             next;           
216         } elsif(/(extern\s+|static\s+)?((struct\s+|union\s+|enum\s+)?\w+((\s*\*)+\s*|\s+))
217             ((__cdecl|__stdcall|CDECL|VFWAPIV|VFWAPI|WINAPIV|WINAPI|CALLBACK)\s+)?
218             (\w+(\(\w+\))?)\s*\(([^\)]*)\)\s*(\{|\;)/sx)
219         {
220             $_ = $'; $again = 1;
221             
222             if($11 eq "{")  {
223                 $level++;
224             }
225
226             my $linkage = $1;
227             my $return_type = $2;
228             my $calling_convention = $7;
229             my $name = $8;
230             my $arguments = $10;
231
232             if(!defined($linkage)) {
233                 $linkage = "";
234             }
235
236             if(!defined($calling_convention)) {
237                 $calling_convention = "";
238             }
239
240             $linkage =~ s/\s*$//;
241
242             $return_type =~ s/\s*$//;
243             $return_type =~ s/\s*\*\s*/*/g;
244             $return_type =~ s/(\*+)/ $1/g;
245
246             if($regs_entrypoints{$name}) {
247                 $name = $regs_entrypoints{$name};
248             } 
249
250             $arguments =~ y/\t\n/  /;
251             $arguments =~ s/^\s*(.*?)\s*$/$1/;
252             if($arguments eq "") { $arguments = "void" }
253
254             my @argument_types;
255             my @argument_names;
256             my @arguments = split(/,/, $arguments);
257             foreach my $n (0..$#arguments) {
258                 my $argument_type = "";
259                 my $argument_name = "";
260                 my $argument = $arguments[$n];
261                 $argument =~ s/^\s*(.*?)\s*$/$1/;
262                 # print "  " . ($n + 1) . ": '$argument'\n";
263                 $argument =~ s/^(IN OUT(?=\s)|IN(?=\s)|OUT(?=\s)|\s*)\s*//;
264                 $argument =~ s/^(const(?=\s)|CONST(?=\s)|\s*)\s*//;
265                 if($argument =~ /^\.\.\.$/) {
266                     $argument_type = "...";
267                     $argument_name = "...";
268                 } elsif($argument =~ /^
269                         ((?:struct\s+|union\s+|enum\s+|(?:signed\s+|unsigned\s+)
270                           (?:short\s+(?=int)|long\s+(?=int))?)?\w+)\s*
271                         ((?:const)?\s*(?:\*\s*?)*)\s*
272                         (?:WINE_UNUSED\s+)?(\w*)\s*(?:\[\]|\s+OPTIONAL)?/x)
273                 {
274                     $argument_type = "$1";
275                     if($2 ne "") {
276                         $argument_type .= " $2";
277                     }
278                     $argument_name = $3;
279
280                     $argument_type =~ s/\s*const\s*/ /;
281                     $argument_type =~ s/^\s*(.*?)\s*$/$1/;
282
283                     $argument_name =~ s/^\s*(.*?)\s*$/$1/;
284                 } else {
285                     die "$file: $.: syntax error: '$argument'\n";
286                 }
287                 $argument_types[$n] = $argument_type;
288                 $argument_names[$n] = $argument_name;
289                 # print "  " . ($n + 1) . ": '" . $argument_types[$n] . "', '" . $argument_names[$n] . "'\n";
290             }
291             if($#argument_types == 0 && $argument_types[0] =~ /^void$/i) {
292                 $#argument_types = -1;
293                 $#argument_names = -1;  
294             }
295
296             if($options->debug) {
297                 print "$file: $return_type $calling_convention $name(" . join(",", @arguments) . ")\n";
298             }
299             
300             &$function_begin($documentation,$linkage,$return_type,$calling_convention,$name,\@argument_types,\@argument_names,\@argument_documentations);
301             if($level == 0) {
302                 &$function_end;
303             }
304         } elsif(/DC_(GET_X_Y|GET_VAL_16)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
305             $_ = $'; $again = 1;
306             my @arguments = ("HDC16");
307             &$function_begin($documentation, "", $2, "WINAPI", $3, \@arguments);
308             &$function_end;
309         } elsif(/DC_(GET_VAL)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,.*?\)/s) {
310             $_ = $'; $again = 1;
311             my $return16 = $3 . "16";
312             my $return32 = $3;
313             my $name16 = $2 . "16";
314             my $name32 = $2;
315             my @arguments16 = ("HDC16");
316             my @arguments32 = ("HDC");
317
318             if($name16 eq "COLORREF16") { $name16 = "COLORREF"; }
319
320             &$function_begin($documentation, "", $name16, "WINAPI", $return16, \@arguments16);
321             &$function_end;
322             &$function_begin($documentation, "", $name32, "WINAPI", $return32, \@arguments32);
323             &$function_end;
324         } elsif(/DC_(GET_VAL_EX)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
325             $_ = $'; $again = 1;
326             my @arguments16 = ("HDC16", "LP" . $5 . "16");
327             my @arguments32 = ("HDC", "LP" . $5);
328             &$function_begin($documentation, "", "BOOL16", "WINAPI", $2 . "16", \@arguments16);
329             &$function_end;
330             &$function_begin($documentation, "", "BOOL", "WINAPI", $2, \@arguments32);
331             &$function_end;
332         } elsif(/DC_(SET_MODE)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
333             $_ = $'; $again = 1;
334             my @arguments16 = ("HDC16", "INT16");
335             my @arguments32 = ("HDC", "INT");
336             &$function_begin($documentation, "", "INT16", "WINAPI", $2 . "16", \@arguments16);
337             &$function_end;
338             &$function_begin($documentation, "", "INT", "WINAPI", $2, \@arguments32);
339             &$function_end;
340         } elsif(/WAVEIN_SHORTCUT_0\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
341             $_ = $'; $again = 1;
342             my @arguments16 = ("HWAVEIN16");
343             my @arguments32 = ("HWAVEIN");
344             &$function_begin($documentation, "", "UINT16", "WINAPI", "waveIn" . $1 . "16", \@arguments16);
345             &$function_end;
346             &$function_begin($documentation, "", "UINT", "WINAPI", "waveIn" . $1, \@arguments32);
347             &$function_end;         
348         } elsif(/WAVEOUT_SHORTCUT_0\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
349             $_ = $'; $again = 1;
350             my @arguments16 = ("HWAVEOUT16");
351             my @arguments32 = ("HWAVEOUT");
352             &$function_begin($documentation, "", "UINT16", "WINAPI", "waveOut" . $1 . "16", \@arguments16);
353             &$function_end;
354             &$function_begin($documentation, "", "UINT", "WINAPI", "waveOut" . $1, \@arguments32);          
355             &$function_end;
356         } elsif(/WAVEOUT_SHORTCUT_(1|2)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
357             $_ = $'; $again = 1;
358             if($1 eq "1") {
359                 my @arguments16 = ("HWAVEOUT16", $4);
360                 my @arguments32 = ("HWAVEOUT", $4);
361                 &$function_begin($documentation, "", "UINT16", "WINAPI", "waveOut" . $2 . "16", \@arguments16);
362                 &$function_end;
363                 &$function_begin($documentation, "", "UINT", "WINAPI", "waveOut" . $2, \@arguments32);
364                 &$function_end;
365             } elsif($1 eq 2) {
366                 my @arguments16 = ("UINT16", $4);
367                 my @arguments32 = ("UINT", $4);
368                 &$function_begin($documentation, "", "UINT16", "WINAPI", "waveOut". $2 . "16", \@arguments16);
369                 &$function_end;
370                 &$function_begin($documentation, "", "UINT", "WINAPI", "waveOut" . $2, \@arguments32);
371                 &$function_end;
372             }
373         } elsif(/DEFINE_REGS_ENTRYPOINT_\d+\(\s*(\S*)\s*,\s*([^\s,\)]*).*?\)/s) {
374             $_ = $'; $again = 1;
375             $regs_entrypoints{$2} = $1;
376         } elsif(/\'[^\']*\'/s) {
377             $_ = $'; $again = 1;
378         } elsif(/\"[^\"]*\"/s) {
379             $_ = $'; $again = 1;
380         } elsif(/;/s) {
381             $_ = $'; $again = 1;
382         } elsif(/extern\s+"C"\s+{/s) {
383             $_ = $'; $again = 1;
384         } elsif(/\{/s) {
385             $_ = $'; $again = 1;
386             print "+1: $_\n" if $options->debug >= 2;
387             $level++;
388         } else {
389             $lookahead = 1;
390         }
391     }
392     close(IN);
393     print STDERR "done\n" if $options->verbose;
394     $output->write("$file: not at toplevel at end of file\n") unless $level == 0;
395 }
396
397 1;