- FreeBSD now supported.
[wine] / tools / winapi / c_parser.pm
1 package c_parser;
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();
11
12 use options qw($options);
13 use output qw($output);
14
15 use c_function;
16
17 ########################################################################
18 # new
19 #
20 sub new {
21     my $proto = shift;
22     my $class = ref($proto) || $proto;
23     my $self  = {};
24     bless ($self, $class);
25
26     my $file = \${$self->{FILE}};
27     my $found_comment = \${$self->{FOUND_COMMENT}};
28     my $found_declaration = \${$self->{FOUND_DECLARATION}};
29     my $create_function = \${$self->{CREATE_FUNCTION}};
30     my $found_function = \${$self->{FOUND_FUNCTION}};
31     my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
32     my $found_line = \${$self->{FOUND_LINE}};
33     my $found_preprocessor = \${$self->{FOUND_PREPROCESSOR}};
34     my $found_statement = \${$self->{FOUND_STATEMENT}};
35     my $found_variable = \${$self->{FOUND_VARIABLE}};
36
37     $$file = shift;
38
39     $$found_comment = sub { return 1; };
40     $$found_declaration = sub { return 1; };
41     $$create_function = sub { return new c_function; };
42     $$found_function = sub { return 1; };
43     $$found_function_call = sub { return 1; };
44     $$found_line = sub { return 1; };
45     $$found_preprocessor = sub { return 1; };
46     $$found_statement = sub { return 1; };
47     $$found_variable = sub { return 1; };
48
49     return $self;
50 }
51
52 ########################################################################
53 # set_found_comment_callback
54 #
55 sub set_found_comment_callback {
56     my $self = shift;
57
58     my $found_comment = \${$self->{FOUND_COMMENT}};
59
60     $$found_comment = shift;
61 }
62
63 ########################################################################
64 # set_found_declaration_callback
65 #
66 sub set_found_declaration_callback {
67     my $self = shift;
68
69     my $found_declaration = \${$self->{FOUND_DECLARATION}};
70
71     $$found_declaration = shift;
72 }
73
74 ########################################################################
75 # set_found_function_callback
76 #
77 sub set_found_function_callback {
78     my $self = shift;
79
80     my $found_function = \${$self->{FOUND_FUNCTION}};
81
82     $$found_function = shift;
83 }
84
85 ########################################################################
86 # set_found_function_call_callback
87 #
88 sub set_found_function_call_callback {
89     my $self = shift;
90
91     my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
92
93     $$found_function_call = shift;
94 }
95
96 ########################################################################
97 # set_found_line_callback
98 #
99 sub set_found_line_callback {
100     my $self = shift;
101
102     my $found_line = \${$self->{FOUND_LINE}};
103
104     $$found_line = shift;
105 }
106
107 ########################################################################
108 # set_found_preprocessor_callback
109 #
110 sub set_found_preprocessor_callback {
111     my $self = shift;
112
113     my $found_preprocessor = \${$self->{FOUND_PREPROCESSOR}};
114
115     $$found_preprocessor = shift;
116 }
117
118 ########################################################################
119 # set_found_statement_callback
120 #
121 sub set_found_statement_callback {
122     my $self = shift;
123
124     my $found_statement = \${$self->{FOUND_STATEMENT}};
125
126     $$found_statement = shift;
127 }
128
129 ########################################################################
130 # set_found_variable_callback
131 #
132 sub set_found_variable_callback {
133     my $self = shift;
134
135     my $found_variable = \${$self->{FOUND_VARIABLE}};
136
137     $$found_variable = shift;
138 }
139
140 ########################################################################
141 # _parse_c
142
143 sub _parse_c {
144     my $self = shift;
145
146     my $pattern = shift;
147     my $refcurrent = shift;
148     my $refline = shift;
149     my $refcolumn = shift;
150
151     my $refmatch = shift;
152
153     local $_ = $$refcurrent;
154     my $line = $$refline;
155     my $column = $$refcolumn;
156
157     my $match;
158     if(s/^(?:$pattern)//s) {
159         $self->_update_c_position($&, \$line, \$column);
160         $match = $&;
161     } else {
162         return 0;
163     }
164
165     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
166
167     $$refcurrent = $_;
168     $$refline = $line;
169     $$refcolumn = $column;
170
171     $$refmatch = $match;
172
173     return 1;
174 }
175
176 ########################################################################
177 # _parse_c_error
178 #
179 # FIXME: Use caller (See man perlfunc)
180
181 sub _parse_c_error {
182     my $self = shift;
183
184     my $file = \${$self->{FILE}};
185
186     local $_ = shift;
187     my $line = shift;
188     my $column = shift;
189     my $context = shift;
190     my $message = shift;
191
192     $message = "parse error" if !$message;
193
194     my $current = "";
195     if($_) {
196         my @lines = split(/\n/, $_);
197
198         $current .= $lines[0] . "\n" if $lines[0];
199         $current .= $lines[1] . "\n" if $lines[1];
200     }
201
202     if($output->prefix) {
203         $output->write("\n");
204         $output->prefix("");
205     }
206     
207     if($current) {
208         $output->write("$$file:$line." . ($column + 1) . ": $context: $message: \\\n$current");
209     } else {
210         $output->write("$$file:$line." . ($column + 1) . ": $context: $message\n");
211     }
212
213     exit 1;
214 }
215
216 ########################################################################
217 # _parse_c_warning
218
219 sub _parse_c_warning {
220     my $self = shift;
221
222     my $line = shift;
223     my $column = shift;
224     my $message = shift;
225
226     $output->write("$line." . ($column + 1) . ": $message\n");
227 }
228
229 ########################################################################
230 # _parse_c_until_one_of
231
232 sub _parse_c_until_one_of {
233     my $self = shift;
234
235     my $characters = shift;
236     my $refcurrent = shift;
237     my $refline = shift;
238     my $refcolumn = shift;
239     my $match = shift;
240
241     local $_ = $$refcurrent;
242     my $line = $$refline;
243     my $column = $$refcolumn;
244
245     if(!defined($match)) {
246         my $blackhole;
247         $match = \$blackhole;
248     }
249
250     $$match = "";
251     while(/^[^$characters]/s) {
252         my $submatch = "";
253
254         if(s/^[^$characters\n\t\'\"]*//s) {
255             $submatch .= $&;
256         }
257
258         if(s/^\'//) {
259             $submatch .= "\'";
260             while(/^./ && !s/^\'//) {
261                 s/^([^\'\\]*)//s;
262                 $submatch .= $1;
263                 if(s/^\\//) {
264                     $submatch .= "\\";
265                     if(s/^(.)//s) {
266                         $submatch .= $1;
267                         if($1 eq "0") {
268                             s/^(\d{0,3})//s;
269                             $submatch .= $1;
270                         }
271                     }
272                 }
273             }
274             $submatch .= "\'";
275
276             $$match .= $submatch;
277             $column += length($submatch);
278         } elsif(s/^\"//) {
279             $submatch .= "\"";
280             while(/^./ && !s/^\"//) {
281                 s/^([^\"\\]*)//s;
282                 $submatch .= $1;
283                 if(s/^\\//) {
284                     $submatch .= "\\";
285                     if(s/^(.)//s) {
286                         $submatch .= $1;
287                         if($1 eq "0") {
288                             s/^(\d{0,3})//s;
289                             $submatch .= $1;
290                         }
291                     }
292                 }
293             }
294             $submatch .= "\"";
295
296             $$match .= $submatch;
297             $column += length($submatch);
298         } elsif(s/^\n//) {
299             $submatch .= "\n";
300
301             $$match .= $submatch;
302             $line++;
303             $column = 0;
304         } elsif(s/^\t//) {
305             $submatch .= "\t";
306
307             $$match .= $submatch;
308             $column = $column + 8 - $column % 8;
309         } else {
310             $$match .= $submatch;
311             $column += length($submatch);
312         }
313     }
314
315     $$refcurrent = $_;
316     $$refline = $line;
317     $$refcolumn = $column;
318     return 1;
319 }
320
321 ########################################################################
322 # _update_c_position
323
324 sub _update_c_position {
325     my $self = shift;
326
327     local $_ = shift;
328     my $refline = shift;
329     my $refcolumn = shift;
330
331     my $line = $$refline;
332     my $column = $$refcolumn;
333
334     while($_) {
335         if(s/^[^\n\t\'\"]*//s) {
336             $column += length($&);
337         }
338
339         if(s/^\'//) {
340             $column++;
341             while(/^./ && !s/^\'//) {
342                 s/^([^\'\\]*)//s;
343                 $column += length($1);
344                 if(s/^\\//) {
345                     $column++;
346                     if(s/^(.)//s) {
347                         $column += length($1);
348                         if($1 eq "0") {
349                             s/^(\d{0,3})//s;
350                             $column += length($1);
351                         }
352                     }
353                 }
354             }
355             $column++;
356         } elsif(s/^\"//) {
357             $column++;
358             while(/^./ && !s/^\"//) {
359                 s/^([^\"\\]*)//s;
360                 $column += length($1);
361                 if(s/^\\//) {
362                     $column++;
363                     if(s/^(.)//s) {
364                         $column += length($1);
365                         if($1 eq "0") {
366                             s/^(\d{0,3})//s;
367                             $column += length($1);
368                         }
369                     }
370                 }
371             }
372             $column++;
373         } elsif(s/^\n//) {
374             $line++;
375             $column = 0;
376         } elsif(s/^\t//) {
377             $column = $column + 8 - $column % 8;
378         }
379     }
380
381     $$refline = $line;
382     $$refcolumn = $column;
383 }
384
385 ########################################################################
386 # parse_c_block
387
388 sub parse_c_block {
389     my $self = shift;
390
391     my $refcurrent = shift;
392     my $refline = shift;
393     my $refcolumn = shift;
394
395     my $refstatements = shift;
396     my $refstatements_line = shift;
397     my $refstatements_column = shift;
398
399     local $_ = $$refcurrent;
400     my $line = $$refline;
401     my $column = $$refcolumn;
402
403     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
404
405     my $statements;
406     if(s/^\{//) {
407         $column++;
408         $statements = "";
409     } else {
410         return 0;
411     }
412
413     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
414
415     my $statements_line = $line;
416     my $statements_column = $column;
417
418     my $plevel = 1;
419     while($plevel > 0) {
420         my $match;
421         $self->_parse_c_until_one_of("\\{\\}", \$_, \$line, \$column, \$match);
422
423         $column++;
424
425         $statements .= $match;
426         if(s/^\}//) {
427             $plevel--;
428             if($plevel > 0) {
429                 $statements .= "}";
430             }
431         } elsif(s/^\{//) {
432             $plevel++;
433             $statements .= "{";
434         } else {
435             return 0;
436         }
437     }
438
439     $$refcurrent = $_;
440     $$refline = $line;
441     $$refcolumn = $column;
442     $$refstatements = $statements;
443     $$refstatements_line = $statements_line;
444     $$refstatements_column = $statements_column;
445
446     return 1;
447 }
448
449 ########################################################################
450 # parse_c_declaration
451
452 sub parse_c_declaration {
453     my $self = shift;
454
455     my $found_declaration = \${$self->{FOUND_DECLARATION}};
456     my $found_function = \${$self->{FOUND_FUNCTION}};
457
458     my $refcurrent = shift;
459     my $refline = shift;
460     my $refcolumn = shift;
461
462     local $_ = $$refcurrent;
463     my $line = $$refline;
464     my $column = $$refcolumn;
465
466     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
467
468     my $begin_line = $line;
469     my $begin_column = $column + 1;
470
471     my $end_line = $begin_line;
472     my $end_column = $begin_column;
473     $self->_update_c_position($_, \$end_line, \$end_column);
474
475     if(!&$$found_declaration($begin_line, $begin_column, $end_line, $end_column, $_)) {
476         return 1;
477     }
478     
479     # Function
480     my $function = shift;
481
482     my $linkage = shift;
483     my $calling_convention = shift;
484     my $return_type = shift;
485     my $name = shift;
486     my @arguments = shift;
487     my @argument_lines = shift;
488     my @argument_columns = shift;
489
490     # Variable
491     my $type;
492
493     if(0) {
494         # Nothing
495     } elsif(s/^(?:DEFAULT|DECLARE)_DEBUG_CHANNEL\s*\(\s*(\w+)\s*\)\s*//s) { # FIXME: Wine specific kludge
496         $self->_update_c_position($&, \$line, \$column);
497     } elsif(s/^__ASM_GLOBAL_FUNC\(\s*(\w+)\s*,\s*//s) { # FIXME: Wine specific kludge
498         $self->_update_c_position($&, \$line, \$column);
499         $self->_parse_c_until_one_of("\)", \$_, \$line, \$column);
500         if(s/\)//) {
501             $column++;
502         }
503     } elsif(s/^(?:jump|strong)_alias//s) { # FIXME: GNU C library specific kludge
504     } elsif(s/^extern\s*\"C\"\s*{//s) {
505         $self->_update_c_position($&, \$line, \$column);
506     } elsif(s/^(?:__asm__|asm)\s*\(//) {
507         $self->_update_c_position($&, \$line, \$column);
508     } elsif($self->parse_c_typedef(\$_, \$line, \$column)) {
509         # Nothing
510     } elsif($self->parse_c_variable(\$_, \$line, \$column, \$linkage, \$type, \$name)) {
511         # Nothing
512     } elsif($self->parse_c_function(\$_, \$line, \$column, \$function)) {
513         if(&$$found_function($function))
514         {
515             my $statements = $function->statements;
516             my $statements_line = $function->statements_line;
517             my $statements_column = $function->statements_column;
518
519             if(defined($statements)) {
520                 if(!$self->parse_c_statements(\$statements, \$statements_line, \$statements_column)) {
521                     return 0;
522                 }
523             }
524         }
525     } else {
526         $self->_parse_c_error($_, $line, $column, "declaration");
527     }
528     
529     $$refcurrent = $_;
530     $$refline = $line;
531     $$refcolumn = $column;
532
533     return 1;
534 }
535
536 ########################################################################
537 # parse_c_declarations
538
539 sub parse_c_declarations {
540     my $self = shift;
541
542     my $refcurrent = shift;
543     my $refline = shift;
544     my $refcolumn = shift;
545
546     return 1;
547 }
548
549 ########################################################################
550 # parse_c_expression
551
552 sub parse_c_expression {
553     my $self = shift;
554
555     my $refcurrent = shift;
556     my $refline = shift;
557     my $refcolumn = shift;
558
559     my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
560
561     local $_ = $$refcurrent;
562     my $line = $$refline;
563     my $column = $$refcolumn;
564
565     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
566
567     while($_) {
568         if(s/^(.*?)(\w+\s*\()/$2/s) {
569             $self->_update_c_position($1, \$line, \$column);
570
571             my $begin_line = $line;
572             my $begin_column = $column + 1;
573             
574             my $name;
575             my @arguments;
576             my @argument_lines;
577             my @argument_columns;
578             if(!$self->parse_c_function_call(\$_, \$line, \$column, \$name, \@arguments, \@argument_lines, \@argument_columns)) {
579                 return 0;
580             }
581             
582             if(&$$found_function_call($begin_line, $begin_column, $line, $column, $name, \@arguments))
583             {
584                 while(defined(my $argument = shift @arguments) &&
585                       defined(my $argument_line = shift @argument_lines) &&
586                       defined(my $argument_column = shift @argument_columns))
587                 {
588                     $self->parse_c_expression(\$argument, \$argument_line, \$argument_column);
589                 }
590             }
591         } else {
592             $_ = "";
593         }
594     }
595
596     $self->_update_c_position($_, \$line, \$column);
597
598     $$refcurrent = $_;
599     $$refline = $line;
600     $$refcolumn = $column;
601
602     return 1;
603 }
604
605 ########################################################################
606 # parse_c_file
607
608 sub parse_c_file {
609     my $self = shift;
610
611     my $found_comment = \${$self->{FOUND_COMMENT}};
612     my $found_line = \${$self->{FOUND_LINE}};
613
614     my $refcurrent = shift;
615     my $refline = shift;
616     my $refcolumn = shift;
617     
618     local $_ = $$refcurrent;
619     my $line = $$refline;
620     my $column = $$refcolumn;
621
622     my $declaration = "";
623     my $declaration_line = $line;
624     my $declaration_column = $column;
625
626     my $previous_line = 0;
627     my $previous_column = -1;
628
629     my $if = 0;
630     my $if0 = 0;
631
632     my $blevel = 1;
633     my $plevel = 1;
634     while($plevel > 0 || $blevel > 0) {
635         my $match;
636         $self->_parse_c_until_one_of("#/\\(\\)\\[\\]\\{\\};", \$_, \$line, \$column, \$match);
637         
638         if($line != $previous_line) {
639             &$$found_line($line);
640         } elsif($column == $previous_column) {
641             $self->_parse_c_error($_, $line, $column, "file", "no progress");
642         } else {
643             # &$$found_line("$line.$column");
644         }
645         $previous_line = $line;
646         $previous_column = $column;
647
648         # $output->write("file: $plevel $blevel: '$match'\n");
649
650         if(!$declaration && $match =~ s/^\s+//s) {
651             $self->_update_c_position($&, \$declaration_line, \$declaration_column);
652         }
653         if(!$if0) {
654             $declaration .= $match;
655         } else {
656             my $blank_lines = 0;
657
658             local $_ = $match;
659             while(s/^.*?\n//) { $blank_lines++; }
660
661             if(!$declaration) {
662                 $declaration_line = $line;
663                 $declaration_column = $column;
664             } else {
665                 $declaration .= "\n" x $blank_lines;
666             }
667
668         }
669
670         if(/^[\#\/]/) {
671             my $blank_lines = 0;
672             if(s/^\#\s*//) {
673                 my $preprocessor_line = $line;
674                 my $preprocessor_column = $column;
675
676                 my $preprocessor = $&;
677                 while(s/^(.*?)\\\s*\n//) {
678                     $blank_lines++;
679                     $preprocessor .= "$1\n";
680                 }
681                 if(s/^(.*?)(\/\*.*?\*\/)(.*?)\n//) {
682                     $_ = "$2\n$_";
683                     if(defined($3)) {
684                         $preprocessor .= "$1$3";
685                     } else {
686                         $preprocessor .= $1;
687                     }
688                 } elsif(s/^(.*?)(\/[\*\/].*?)?\n//) {
689                     if(defined($2)) {
690                         $_ = "$2\n$_";
691                     } else {
692                         $blank_lines++;
693                     }
694                     $preprocessor .= $1;
695                 }
696
697                 if($if0 && $preprocessor =~ /^\#\s*endif/) {
698                     if($if0 > 0) {
699                         if($if > 0) {
700                             $if--;
701                         } else {
702                             $if0--;
703                         }
704                     }
705                 } elsif($preprocessor =~ /^\#\s*if/) {
706                     if($preprocessor =~ /^\#\s*if\s*0/) {
707                         $if0++;
708                     } elsif($if0 > 0) {
709                         $if++;
710                     }
711                 }
712
713                 if(!$self->parse_c_preprocessor(\$preprocessor, \$preprocessor_line, \$preprocessor_column)) {
714                      return 0;
715                 }
716             }
717
718             if(s/^\/\*.*?\*\///s) {
719                 &$$found_comment($line, $column + 1, $&);
720                 local $_ = $&;
721                 while(s/^.*?\n//) {
722                     $blank_lines++; 
723                 }
724                 if($_) {
725                     $column += length($_);
726                 }
727             } elsif(s/^\/\/(.*?)\n//) {
728                 &$$found_comment($line, $column + 1, $&);
729                 $blank_lines++;
730             } elsif(s/^\///) {
731                 if(!$if0) {
732                     $declaration .= $&;
733                     $column++;
734                 }
735             }
736
737             $line += $blank_lines;
738             if($blank_lines > 0) {
739                 $column = 0;
740             }
741
742             if(!$declaration) {
743                 $declaration_line = $line;
744                 $declaration_column = $column;
745             } elsif($blank_lines > 0) {
746                 $declaration .= "\n" x $blank_lines;
747             }
748
749             next;
750         }
751
752         $column++;
753
754         if($if0) {
755             s/^.//;
756             next;
757         }
758
759         if(s/^[\(\[]//) {
760             $plevel++;
761             $declaration .= $&;
762         } elsif(s/^\]//) {
763             $plevel--;
764             $declaration .= $&;
765         } elsif(s/^\)//) {
766             $plevel--;
767             $declaration .= $&;
768             if($plevel == 1 && $declaration =~ /^__ASM_GLOBAL_FUNC/) {
769                 if(!$self->parse_c_declaration(\$declaration, \$declaration_line, \$declaration_column)) {
770                     return 0;
771                 }
772                 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
773                 $declaration = "";
774                 $declaration_line = $line;
775                 $declaration_column = $column;
776             }
777         } elsif(s/^\{//) {
778             $blevel++;
779             $declaration .= $&;
780         } elsif(s/^\}//) {
781             $blevel--;
782             $declaration .= $&;
783             if($declaration =~ /^typedef/s ||
784                $declaration =~ /^(?:const\s+|extern\s+|static\s+)*(?:struct|union)(?:\s+\w+)?\s*\{/s)
785             {
786                 # Nothing
787             } elsif($plevel == 1 && $blevel == 1) {
788                 if(!$self->parse_c_declaration(\$declaration, \$declaration_line, \$declaration_column)) {
789                     return 0;
790                 }
791                 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
792                 $declaration = "";
793                 $declaration_line = $line;
794                 $declaration_column = $column;
795             } elsif($column == 1) {
796                 $self->_parse_c_error("", $line, $column, "file", "inner } ends on column 1");
797             }
798         } elsif(s/^;//) {
799             $declaration .= $&;
800             if($blevel == 1 && 
801                $declaration !~ /^typedef/ && 
802                $declaration !~ /^(?:const\s+|extern\s+|static\s+)(?:struct|union)(?:\s+\w+)?\s*\{/s &&
803                $declaration =~ /^(?:\w+\s*)*(?:(?:\*\s*)+|\s+)(\w+)\s*\(\s*(?:(?:\w+\s*,\s*)*\w+\s*)?\)(.*?);/s && 
804                $1 ne "ICOM_VTABLE" && $2) # K&R 
805             {
806                 $self->_parse_c_warning($line, $column, "function $1: warning: function has K&R format");
807             } elsif($plevel == 1 && $blevel == 1) {
808                 $declaration =~ s/\s*;$//;
809                 if($declaration && !$self->parse_c_declaration(\$declaration, \$declaration_line, \$declaration_column)) {
810                     return 0;
811                 }
812                 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
813                 $declaration = "";
814                 $declaration_line = $line;
815                 $declaration_column = $column;
816             }
817         } elsif(/^\s*$/ && $declaration =~ /^\s*$/ && $match =~ /^\s*$/) {
818             $plevel = 0;
819             $blevel = 0;
820         } else {
821             $self->_parse_c_error($_, $line, $column, "file", "'$declaration' '$match'");
822         }
823     }
824
825     $$refcurrent = $_;
826     $$refline = $line;
827     $$refcolumn = $column;
828
829     return 1;
830 }
831
832 ########################################################################
833 # parse_c_function
834
835 sub parse_c_function {
836     my $self = shift;
837
838     my $file = \${$self->{FILE}};
839     my $create_function = \${$self->{CREATE_FUNCTION}};
840
841     my $refcurrent = shift;
842     my $refline = shift;
843     my $refcolumn = shift;
844
845     my $reffunction = shift;
846     
847     local $_ = $$refcurrent;
848     my $line = $$refline;
849     my $column = $$refcolumn;
850
851     my $linkage = "";
852     my $calling_convention = "";
853     my $return_type;
854     my $name;
855     my @arguments;
856     my @argument_lines;
857     my @argument_columns;
858     my $statements;
859     my $statements_line;
860     my $statements_column;
861
862     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
863
864     my $begin_line = $line;
865     my $begin_column = $column + 1;
866
867     my $match;
868     while($self->_parse_c('const|inline|extern|static|volatile|' . 
869                           'signed(?=\\s+char|s+int|\s+long(?:\s+long)?|\s+short)|' .
870                           'unsigned(?=\s+char|\s+int|\s+long(?:\s+long)?|\s+short)',
871                           \$_, \$line, \$column, \$match)) 
872     {
873         if($match =~ /^extern|static$/) {
874             if(!$linkage) {
875                 $linkage = $match;
876             }
877         }
878     }
879
880
881     if(0) {
882         # Nothing
883     } elsif($self->_parse_c('DECL_GLOBAL_CONSTRUCTOR', \$_, \$line, \$column, \$name)) { # FIXME: Wine specific kludge
884         # Nothing
885     } elsif($self->_parse_c('WINE_EXCEPTION_FILTER\(\w+\)', \$_, \$line, \$column, \$name)) { # FIXME: Wine specific kludge
886         # Nothing
887     } else {
888         if(!$self->parse_c_type(\$_, \$line, \$column, \$return_type)) {
889             return 0;
890         }
891
892         $self->_parse_c("__cdecl|__stdcall|inline|CDECL|VFWAPIV|VFWAPI|WINAPIV|WINAPI|CALLBACK|WINE_UNUSED|PASCAL", 
893                         \$_, \$line, \$column, \$calling_convention);
894         
895         if(!$self->_parse_c('\w+', \$_, \$line, \$column, \$name)) { 
896             return 0; 
897         }
898
899         if(!$self->parse_c_tuple(\$_, \$line, \$column, \@arguments, \@argument_lines, \@argument_columns)) {
900             return 0; 
901         }
902     }
903
904     my $kar;
905     # FIXME: Implement proper handling of K&R C functions
906     $self->_parse_c_until_one_of("{", \$_, \$line, \$column, $kar);
907
908     if($kar) {
909         $output->write("K&R: $kar\n");
910     }
911
912     if($_ && !$self->parse_c_block(\$_, \$line, \$column, \$statements, \$statements_line, \$statements_column)) {
913         return 0;
914     }
915
916     my $end_line = $line;
917     my $end_column = $column;
918     
919     $$refcurrent = $_;
920     $$refline = $line;
921     $$refcolumn = $column;
922
923     my $function = &$$create_function;
924
925     $function->file($$file);
926     $function->begin_line($begin_line);
927     $function->begin_column($begin_column);
928     $function->end_line($end_line);
929     $function->end_column($end_column);
930     $function->linkage($linkage);
931     $function->return_type($return_type); 
932     $function->calling_convention($calling_convention);
933     $function->name($name);
934     # if(defined($argument_types)) {
935     #     $function->argument_types([@$argument_types]);
936     # }
937     # if(defined($argument_names)) {
938     #     $function->argument_names([@$argument_names]);
939     # }
940     $function->statements_line($statements_line);
941     $function->statements_column($statements_column);
942     $function->statements($statements);
943
944     $$reffunction = $function;
945
946     return 1;
947 }
948
949 ########################################################################
950 # parse_c_function_call
951
952 sub parse_c_function_call {
953     my $self = shift;
954
955     my $refcurrent = shift;
956     my $refline = shift;
957     my $refcolumn = shift;
958
959     my $refname = shift;
960     my $refarguments = shift;
961     my $refargument_lines = shift;
962     my $refargument_columns = shift;
963
964     local $_ = $$refcurrent;
965     my $line = $$refline;
966     my $column = $$refcolumn;
967
968     my $name;
969     my @arguments;
970     my @argument_lines;
971     my @argument_columns;
972
973     if(s/^(\w+)(\s*)(?=\()//s) {
974         $self->_update_c_position($&, \$line, \$column);
975
976         $name = $1;
977
978         if(!$self->parse_c_tuple(\$_, \$line, \$column, \@arguments, \@argument_lines, \@argument_columns)) {
979             return 0;
980         }
981     } else {
982         return 0;
983     }
984
985     $$refcurrent = $_;
986     $$refline = $line;
987     $$refcolumn = $column;
988
989     $$refname = $name;
990     @$refarguments = @arguments;
991     @$refargument_lines = @argument_lines;
992     @$refargument_columns = @argument_columns;
993
994     return 1;
995 }
996
997 ########################################################################
998 # parse_c_preprocessor
999
1000 sub parse_c_preprocessor {
1001     my $self = shift;
1002
1003     my $found_preprocessor = \${$self->{FOUND_PREPROCESSOR}};
1004
1005     my $refcurrent = shift;
1006     my $refline = shift;
1007     my $refcolumn = shift;
1008
1009     local $_ = $$refcurrent;
1010     my $line = $$refline;
1011     my $column = $$refcolumn;
1012
1013     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1014
1015     my $begin_line = $line;
1016     my $begin_column = $column + 1;
1017
1018     if(!&$$found_preprocessor($begin_line, $begin_column, "$_")) {
1019         return 1;
1020     }
1021     
1022     if(0) {
1023         # Nothing
1024     } elsif(/^\#\s*define\s*(.*?)$/s) {
1025         $self->_update_c_position($_, \$line, \$column);
1026     } elsif(/^\#\s*else/s) {
1027         $self->_update_c_position($_, \$line, \$column);
1028     } elsif(/^\#\s*endif/s) {
1029         $self->_update_c_position($_, \$line, \$column);
1030     } elsif(/^\#\s*(?:if|ifdef|ifndef)?\s*(.*?)$/s) {
1031         $self->_update_c_position($_, \$line, \$column);
1032     } elsif(/^\#\s*include\s+(.*?)$/s) {
1033         $self->_update_c_position($_, \$line, \$column);
1034     } elsif(/^\#\s*undef\s+(.*?)$/s) {
1035         $self->_update_c_position($_, \$line, \$column);
1036     } else {
1037         $self->_parse_c_error($_, $line, $column, "preprocessor");
1038     }
1039
1040     $$refcurrent = $_;
1041     $$refline = $line;
1042     $$refcolumn = $column;
1043
1044     return 1;
1045 }
1046
1047 ########################################################################
1048 # parse_c_statement
1049
1050 sub parse_c_statement {
1051     my $self = shift;
1052
1053     my $refcurrent = shift;
1054     my $refline = shift;
1055     my $refcolumn = shift;
1056
1057     my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
1058
1059     local $_ = $$refcurrent;
1060     my $line = $$refline;
1061     my $column = $$refcolumn;
1062
1063     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1064
1065     $self->_parse_c('(?:case\s+)?(\w+)\s*:\s*', \$_, \$line, \$column);
1066
1067     # $output->write("$line.$column: statement: '$_'\n");
1068
1069     if(/^$/) {
1070         # Nothing
1071     } elsif(/^\{/) {
1072         my $statements;
1073         my $statements_line;
1074         my $statements_column;
1075         if(!$self->parse_c_block(\$_, \$line, \$column, \$statements, \$statements_line, \$statements_column)) {
1076             return 0;
1077         }
1078         if(!$self->parse_c_statements(\$statements, \$statements_line, \$statements_column)) {
1079             return 0;
1080         }
1081     } elsif(s/^(for|if|switch|while)\s*(?=\()//) {
1082         $self->_update_c_position($&, \$line, \$column);
1083
1084         my $name = $1;
1085
1086         my @arguments;
1087         my @argument_lines;
1088         my @argument_columns;
1089         if(!$self->parse_c_tuple(\$_, \$line, \$column, \@arguments, \@argument_lines, \@argument_columns)) {
1090             return 0;
1091         }
1092
1093         $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1094         if(!$self->parse_c_statement(\$_, \$line, \$column)) {
1095             return 0;
1096         }
1097         $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1098
1099         while(defined(my $argument = shift @arguments) &&
1100               defined(my $argument_line = shift @argument_lines) &&
1101               defined(my $argument_column = shift @argument_columns))
1102         {
1103             $self->parse_c_expression(\$argument, \$argument_line, \$argument_column);
1104         }
1105     } elsif(s/^else//) {
1106         $self->_update_c_position($&, \$line, \$column);
1107         if(!$self->parse_c_statement(\$_, \$line, \$column)) {
1108             return 0;
1109         }
1110     } elsif(s/^return//) {
1111         $self->_update_c_position($&, \$line, \$column);
1112         $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1113         if(!$self->parse_c_expression(\$_, \$line, \$column)) {
1114             return 0;
1115         }
1116     } elsif($self->parse_c_expression(\$_, \$line, \$column)) {
1117         # Nothing
1118     } else {
1119         # $self->_parse_c_error($_, $line, $column, "statement");
1120     }
1121
1122     $self->_update_c_position($_, \$line, \$column);
1123
1124     $$refcurrent = $_;
1125     $$refline = $line;
1126     $$refcolumn = $column;
1127
1128     return 1;
1129 }
1130
1131 ########################################################################
1132 # parse_c_statements
1133
1134 sub parse_c_statements {
1135     my $self = shift;
1136
1137     my $refcurrent = shift;
1138     my $refline = shift;
1139     my $refcolumn = shift;
1140
1141     my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
1142
1143     local $_ = $$refcurrent;
1144     my $line = $$refline;
1145     my $column = $$refcolumn;
1146
1147     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1148
1149     # $output->write("$line.$column: statements: '$_'\n");
1150
1151     my $statement = "";
1152     my $statement_line = $line;
1153     my $statement_column = $column;
1154
1155     my $previous_line = -1;
1156     my $previous_column = -1;
1157
1158     my $blevel = 1;
1159     my $plevel = 1;
1160     while($plevel > 0 || $blevel > 0) {
1161         my $match;
1162         $self->_parse_c_until_one_of("\\(\\)\\[\\]\\{\\};", \$_, \$line, \$column, \$match);
1163
1164         if($previous_line == $line && $previous_column == $column) {
1165             $self->_parse_c_error($_, $line, $column, "statements", "no progress");
1166         }
1167         $previous_line = $line;
1168         $previous_column = $column;
1169
1170         # $output->write("'$match' '$_'\n");
1171
1172         $statement .= $match;
1173         $column++;
1174         if(s/^[\(\[]//) {
1175             $plevel++;
1176             $statement .= $&;
1177         } elsif(s/^[\)\]]//) {
1178             $plevel--;
1179             if($plevel <= 0) {
1180                 $self->_parse_c_error($_, $line, $column, "statements");
1181             }
1182             $statement .= $&;
1183         } elsif(s/^\{//) {
1184             $blevel++;
1185             $statement .= $&;
1186         } elsif(s/^\}//) {
1187             $blevel--;
1188             $statement .= $&;
1189             if($blevel == 1) {
1190                 if(!$self->parse_c_statement(\$statement, \$statement_line, \$statement_column)) {
1191                     return 0;
1192                 }
1193                 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1194                 $statement = "";
1195                 $statement_line = $line;
1196                 $statement_column = $column;
1197             }
1198         } elsif(s/^;//) {
1199             if($plevel == 1 && $blevel == 1) {
1200                 if(!$self->parse_c_statement(\$statement, \$statement_line, \$statement_column)) {
1201                     return 0;
1202                 }
1203
1204                 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1205                 $statement = "";
1206                 $statement_line = $line;
1207                 $statement_column = $column;
1208             } else {
1209                 $statement .= $&;
1210             }
1211         } elsif(/^\s*$/ && $statement =~ /^\s*$/ && $match =~ /^\s*$/) {
1212             $plevel = 0;
1213             $blevel = 0;
1214         } else {
1215             $self->_parse_c_error($_, $line, $column, "statements");
1216         }
1217     }
1218
1219     $self->_update_c_position($_, \$line, \$column);
1220
1221     $$refcurrent = $_;
1222     $$refline = $line;
1223     $$refcolumn = $column;
1224
1225     return 1;
1226 }
1227
1228 ########################################################################
1229 # parse_c_tuple
1230
1231 sub parse_c_tuple {
1232     my $self = shift;
1233
1234     my $refcurrent = shift;
1235     my $refline = shift;
1236     my $refcolumn = shift;
1237
1238     # FIXME: Should not write directly
1239     my $items = shift;
1240     my $item_lines = shift;
1241     my $item_columns = shift;
1242
1243     local $_ = $$refcurrent;
1244
1245     my $line = $$refline;
1246     my $column = $$refcolumn;
1247
1248     my $item;
1249     if(s/^\(//) {
1250         $column++;
1251         $item = "";
1252     } else {
1253         return 0;
1254     }
1255
1256     my $item_line = $line;
1257     my $item_column = $column + 1;
1258
1259     my $plevel = 1;
1260     while($plevel > 0) {
1261         my $match;
1262         $self->_parse_c_until_one_of("\\(,\\)", \$_, \$line, \$column, \$match);
1263
1264         $column++;
1265
1266         $item .= $match;
1267         if(s/^\)//) {
1268             $plevel--;
1269             if($plevel == 0) {
1270                 push @$item_lines, $item_line;
1271                 push @$item_columns, $item_column;
1272                 push @$items, $item;
1273                 $item = "";
1274             } else {
1275                 $item .= ")";
1276             }
1277         } elsif(s/^\(//) {
1278             $plevel++;
1279             $item .= "(";
1280         } elsif(s/^,//) {
1281             if($plevel == 1) {
1282                 push @$item_lines, $item_line;
1283                 push @$item_columns, $item_column;
1284                 push @$items, $item;
1285                 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1286                 $item_line = $line;
1287                 $item_column = $column + 1;
1288                 $item = "";
1289             } else {
1290                 $item .= ",";
1291             }
1292         } else {
1293             return 0;
1294         }
1295     }
1296
1297     $$refcurrent = $_;
1298     $$refline = $line;
1299     $$refcolumn = $column;
1300
1301     return 1;
1302 }
1303
1304 ########################################################################
1305 # parse_c_type
1306
1307 sub parse_c_type {
1308     my $self = shift;
1309
1310     my $refcurrent = shift;
1311     my $refline = shift;
1312     my $refcolumn = shift;
1313
1314     my $reftype = shift;
1315
1316     local $_ = $$refcurrent;
1317     my $line = $$refline;
1318     my $column = $$refcolumn;
1319
1320     my $type;
1321
1322     $self->_parse_c("const", \$_, \$line, \$column);
1323
1324     if(0) {
1325         # Nothing
1326     } elsif($self->_parse_c('ICOM_VTABLE\(.*?\)', \$_, \$line, \$column, \$type)) {
1327         # Nothing
1328     } elsif($self->_parse_c('(?:enum\s+|struct\s+|union\s+)?\w+\s*(\*\s*)*', \$_, \$line, \$column, \$type)) {
1329         # Nothing
1330     } else {
1331         return 0;
1332     }
1333     $type =~ s/\s//g;
1334
1335     $$refcurrent = $_;
1336     $$refline = $line;
1337     $$refcolumn = $column;
1338
1339     $$reftype = $type;
1340
1341     return 1;
1342 }
1343
1344 ########################################################################
1345 # parse_c_typedef
1346
1347 sub parse_c_typedef {
1348     my $self = shift;
1349
1350     my $refcurrent = shift;
1351     my $refline = shift;
1352     my $refcolumn = shift;
1353
1354     my $reftype = shift;
1355
1356     local $_ = $$refcurrent;
1357     my $line = $$refline;
1358     my $column = $$refcolumn;
1359
1360     my $type;
1361
1362     if($self->_parse_c("typedef", \$_, \$line, \$column)) {
1363         # Nothing
1364     } elsif($self->_parse_c('enum(?:\s+\w+)?\s*\{', \$_, \$line, \$column)) {
1365         # Nothing
1366     } else {
1367         return 0;
1368     }
1369
1370     $$refcurrent = $_;
1371     $$refline = $line;
1372     $$refcolumn = $column;
1373
1374     $$reftype = $type;
1375
1376     return 1;
1377 }
1378
1379 ########################################################################
1380 # parse_c_variable
1381
1382 sub parse_c_variable {
1383     my $self = shift;
1384
1385     my $found_variable = \${$self->{FOUND_VARIABLE}};
1386
1387     my $refcurrent = shift;
1388     my $refline = shift;
1389     my $refcolumn = shift;
1390
1391     my $reflinkage = shift;
1392     my $reftype = shift;
1393     my $refname = shift;
1394
1395     local $_ = $$refcurrent;
1396     my $line = $$refline;
1397     my $column = $$refcolumn;
1398
1399     $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1400
1401     my $begin_line = $line;
1402     my $begin_column = $column + 1;
1403
1404     my $linkage = "";
1405     my $type = "";
1406     my $name = "";
1407
1408     my $match;
1409     while($self->_parse_c('const|inline|extern|static|volatile|' . 
1410                           'signed(?=\\s+char|s+int|\s+long(?:\s+long)?|\s+short)|' .
1411                           'unsigned(?=\s+char|\s+int|\s+long(?:\s+long)?|\s+short)',
1412                           \$_, \$line, \$column, \$match)) 
1413     {
1414         if($match =~ /^extern|static$/) {
1415             if(!$linkage) {
1416                 $linkage = $match;
1417             }
1418         }
1419     }
1420
1421     my $finished = 0;
1422
1423     if($finished) {
1424         # Nothing
1425     } elsif($self->_parse_c('SEQ_DEFINEBUF', \$_, \$line, \$column, \$match)) { # Linux specific
1426         $type = $match;
1427         $finished = 1;
1428     } elsif($self->_parse_c('DEFINE_GUID', \$_, \$line, \$column, \$match)) { # Windows specific
1429         $type = $match;
1430         $finished = 1;
1431     } elsif($self->_parse_c('DEFINE_REGS_ENTRYPOINT_\w+|DPQ_DECL_\w+|HANDLER_DEF|IX86_ONLY', # Wine specific
1432                             \$_, \$line, \$column, \$match))
1433     { 
1434         $type = $match;
1435         $finished = 1;
1436     } elsif($self->_parse_c('(?:struct\s+)?ICOM_VTABLE\s*\(\w+\)', \$_, \$line, \$column, \$match)) {
1437         $type = $match;
1438         $finished = 1;
1439     } elsif(s/^(?:enum\s+|struct\s+|union\s+)(\w+)?\s*\{.*?\}\s*//s) {
1440         $self->_update_c_position($&, \$line, \$column);
1441
1442         if(defined($1)) {
1443             $type = "struct $1 { }";
1444         } else {
1445             $type = "struct { }";
1446         }
1447         if(defined($2)) {
1448             my $stars = $2;
1449             $stars =~ s/\s//g;
1450             if($stars) {
1451                 $type .= " $type";
1452             }
1453         }
1454     } elsif(s/^((?:enum\s+|struct\s+|union\s+)?\w+)\s*(?:\*\s*)*//s) {
1455         $type = $&;
1456         $type =~ s/\s//g;
1457     } else {
1458         return 0;
1459     }
1460
1461     # $output->write("$type: '$_'\n");
1462
1463     if($finished) {
1464         # Nothing
1465     } elsif(s/^WINAPI\s*//) {
1466         $self->_update_c_position($&, \$line, \$column);
1467     }
1468
1469     if($finished) {
1470         # Nothing
1471     } elsif(s/^(\((?:__cdecl)?\s*\*?\s*(?:__cdecl)?\w+\s*(?:\[[^\]]*\]\s*)*\))\s*\(//) {
1472         $self->_update_c_position($&, \$line, \$column);
1473
1474         $name = $1;
1475         $name =~ s/\s//g;
1476
1477         $self->_parse_c_until_one_of("\\)", \$_, \$line, \$column);
1478         if(s/^\)//) { $column++; }
1479         $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1480
1481         if(!s/^(?:=\s*|,\s*|$)//) {
1482             return 0;
1483         }
1484     } elsif(s/^(?:\*\s*)*(?:const\s+)?(\w+)\s*(?:\[[^\]]*\]\s*)*\s*(?:=\s*|,\s*|$)//) {
1485         $self->_update_c_position($&, \$line, \$column);
1486
1487         $name = $1;
1488         $name =~ s/\s//g;
1489     } elsif(/^$/) {
1490         $name = "";
1491     } else {
1492         return 0;
1493     }
1494  
1495     # $output->write("$type: $name: '$_'\n");
1496
1497     if(1) {
1498         # Nothing
1499     } elsif($self->_parse_c('(?:struct\s+)?ICOM_VTABLE\s*\(.*?\)', \$_, \$line, \$column, \$match)) {
1500         $type = "<type>";
1501         $name = "<name>";
1502     } elsif(s/^((?:enum\s+|struct\s+|union\s+)?\w+)\s*
1503                 (?:\*\s*)*(\w+|\s*\*?\s*\w+\s*\))\s*(?:\[[^\]]*\]|\([^\)]*\))?
1504                 (?:,\s*(?:\*\s*)*(\w+)\s*(?:\[[^\]]*\])?)*
1505             \s*(?:=|$)//sx)
1506     {
1507         $self->_update_c_position($&, \$line, \$column);
1508
1509         $type = $1;
1510         $name = $2;
1511
1512         $type =~ s/\s//g;
1513         $type =~ s/^struct/struct /;
1514     } elsif(/^(?:enum|struct|union)(?:\s+(\w+))?\s*\{.*?\}\s*((?:\*\s*)*)(\w+)\s*(?:=|$)/s) {
1515         $self->_update_c_position($&, \$line, \$column);
1516
1517         if(defined($1)) {
1518             $type = "struct $1 { }";
1519         } else {
1520             $type = "struct { }";
1521         }
1522         my $stars = $2;
1523         $stars =~ s/\s//g;
1524         if($stars) {
1525             $type .= " $type";
1526         }
1527
1528         $name = $3;
1529     } else {
1530         return 0;
1531     }
1532
1533     if(!$name) {
1534         $name = "<name>";
1535     }
1536
1537     $$refcurrent = $_;
1538     $$refline = $line;
1539     $$refcolumn = $column;
1540
1541     $$reflinkage = $linkage;
1542     $$reftype = $type;
1543     $$refname = $name;
1544
1545     if(&$$found_variable($begin_line, $begin_column, $linkage, $type, $name))
1546     {
1547         # Nothing
1548     }
1549
1550     return 1;
1551 }
1552
1553 1;