~ubuntu-branches/ubuntu/edgy/libapache2-mod-perl2/edgy-updates

« back to all changes in this revision

Viewing changes to lib/ModPerl/CScan.pm

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2004-08-19 06:23:48 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040819062348-jxl4koqbtvgm8v2t
Tags: 1.99.14-4
Remove the LFS CFLAGS, and build-dep against apache2-*-dev (>= 2.0.50-10)
as we're backing out of the apache2/apr ABI transition.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package ModPerl::CScan;
 
2
 
 
3
require Exporter;
 
4
use Config '%Config';
 
5
use File::Basename;
 
6
use Data::Flow qw(0.05);
 
7
use strict;                     # Earlier it catches ISA and EXPORT.
 
8
 
 
9
@ModPerl::CScan::ISA = qw(Exporter Data::Flow);
 
10
 
 
11
# Items to export into callers namespace by default. Note: do not export
 
12
# names by default without a very good reason. Use EXPORT_OK instead.
 
13
# Do not simply export all your public functions/methods/constants.
 
14
 
 
15
@ModPerl::CScan::EXPORT = qw(
 
16
            );
 
17
@ModPerl::CScan::EXPORT_OK = qw(
 
18
                        );
 
19
# this flag tells cpp to only output macros
 
20
$ModPerl::CScan::MACROS_ONLY = '-dM';
 
21
 
 
22
$ModPerl::CScan::VERSION = '0.75';
 
23
 
 
24
my (%keywords,%style_keywords);
 
25
for (qw(asm auto break case char continue default do double else enum
 
26
        extern float for fortran goto if int long register return short
 
27
        sizeof static struct switch typedef union unsigned signed while void volatile)) {
 
28
  $keywords{$_}++;
 
29
}
 
30
for (qw(bool class const delete friend inline new operator overload private
 
31
        protected public virtual)) {
 
32
  $style_keywords{'C++'}{$_}++;
 
33
}
 
34
for (qw(__func__ _Complex _Imaginary _Bool inline restrict)) {
 
35
  $style_keywords{'C9X'}{$_}++;
 
36
}
 
37
for (qw(inline const asm noreturn section 
 
38
        constructor destructor unused weak)) {
 
39
  $style_keywords{'GNU'}{$_}++;
 
40
  $style_keywords{'GNU'}{"__$ {_}__"}++;
 
41
}
 
42
  $style_keywords{'GNU'}{__attribute__}++;
 
43
  $style_keywords{'GNU'}{__extension__}++;
 
44
  $style_keywords{'GNU'}{__consts}++;
 
45
  $style_keywords{'GNU'}{__const}++;
 
46
  $style_keywords{'GNU'}{__restrict}++;
 
47
 
 
48
my $recipes
 
49
  = { Defines => { default => '' },
 
50
      cppstdin => { default => $Config{cppstdin} },
 
51
      cppflags => { default => $Config{cppflags} },
 
52
      cppminus => { default => $Config{cppminus} },
 
53
      c_styles => { default => [qw(C++ GNU C9X)] },
 
54
      add_cppflags => { default => '' },
 
55
      keywords => { prerequisites => ['c_styles'],
 
56
                    output => sub {
 
57
                      my %kw = %keywords;
 
58
                      my %add;
 
59
                      for ( @{ shift->{c_styles} } ) {
 
60
                        %add = %{ $style_keywords{$_} };
 
61
                        %kw = (%kw, %add);
 
62
                      }
 
63
                      \%kw;
 
64
                    }, },
 
65
      'undef' => { default => undef },
 
66
      filename_filter => { default => undef },
 
67
      full_text => { class_filter => [ 'text', 'C::Preprocessed',
 
68
                                       qw(undef filename Defines includeDirs Cpp)] },
 
69
      text => { class_filter => [ 'text', 'C::Preprocessed',
 
70
                                  qw(filename_filter filename Defines includeDirs Cpp)] },
 
71
      text_only_from => { class_filter => [ 'text_only_from', 'C::Preprocessed',
 
72
                                            qw(filename_filter filename Defines includeDirs Cpp)] },
 
73
      includes => { filter => [ \&includes, 
 
74
                                qw(filename Defines includeDirs Cpp) ], },
 
75
      includeDirs =>  { prerequisites => ['filedir'], 
 
76
                        output => sub {
 
77
                          my $data = shift;
 
78
                          [ $data->{filedir}, '/usr/local/include', '.'];
 
79
                        } },
 
80
      Cpp => { prerequisites => [qw(cppminus add_cppflags cppflags cppstdin)], 
 
81
               output => sub {
 
82
                 my $data = shift;
 
83
                 return { cppstdin => $data->{cppstdin},
 
84
                          cppflags => "$data->{cppflags} $data->{add_cppflags}",
 
85
                          cppminus => $data->{cppminus} };
 
86
               } },
 
87
      filedir => { output => sub { dirname ( shift->{filename} || '.' ) } },
 
88
      sanitized => { filter => [ \&sanitize, 'text'], },
 
89
      toplevel => { filter => [ \&top_level, 'sanitized'], },
 
90
      full_sanitized => { filter => [ \&sanitize, 'full_text'], },
 
91
      full_toplevel => { filter => [ \&top_level, 'full_sanitized'], },
 
92
      no_type_decl => { filter => [ \&remove_type_decl, 'toplevel'], },
 
93
      typedef_chunks => { filter => [ \&typedef_chunks, 'full_toplevel'], },
 
94
      struct_chunks => { filter => [ \&struct_chunks, 'full_toplevel'], },
 
95
      typedefs_whited => { filter => [ \&typedefs_whited,
 
96
                                       'full_sanitized', 'typedef_chunks',
 
97
                                       'keywords_rex'], },
 
98
      typedef_texts => { filter => [ \&typedef_texts,
 
99
                                     'full_text', 'typedef_chunks'], },
 
100
      struct_texts => { filter => [ \&typedef_texts,
 
101
                                    'full_text', 'struct_chunks'], },
 
102
      typedef_hash => { filter => [ \&typedef_hash,
 
103
                                    'typedef_texts', 'typedefs_whited'], },
 
104
      typedef_structs => { filter => [ \&typedef_structs,
 
105
                                       'typedef_hash', 'struct_texts'], },
 
106
      typedefs_maybe => { filter => [ sub {[keys %{+shift}]},
 
107
                                      'typedef_hash'], },
 
108
      defines_maybe => { filter => [ \&defines_maybe, 'filename'], },
 
109
      defines_no_args => { prerequisites => ['defines_maybe'],
 
110
                           output => sub { shift->{defines_maybe}->[0] }, },
 
111
      defines_args => { prerequisites => ['defines_maybe'],
 
112
                        output => sub { shift->{defines_maybe}->[1] }, },
 
113
 
 
114
      defines_full => { filter => [ \&defines_full, 
 
115
                                    qw(filename Defines includeDirs Cpp) ], },
 
116
      defines_no_args_full => { prerequisites => ['defines_full'],
 
117
                                output => sub { shift->{defines_full}->[0] }, },
 
118
      defines_args_full => { prerequisites => ['defines_full'],
 
119
                        output => sub { shift->{defines_full}->[1] }, },
 
120
 
 
121
      decl_inlines => { filter => [ \&functions_in, 'no_type_decl'], },
 
122
      inline_chunks => { filter => [ sub { shift->[0] }, 'decl_inlines'], },
 
123
      inlines => { filter => [ \&from_chunks, 'inline_chunks', 'text'], },
 
124
      decl_chunks => { filter => [ sub { shift->[1] }, 'decl_inlines'], },
 
125
      decls => { filter => [ \&from_chunks, 'decl_chunks', 'text'], },
 
126
      fdecl_chunks => { filter => [ sub { shift->[4] }, 'decl_inlines'], },
 
127
      fdecls => { filter => [ \&from_chunks, 'fdecl_chunks', 'text'], },
 
128
      mdecl_chunks => { filter => [ sub { shift->[2] }, 'decl_inlines'], },
 
129
      mdecls => { filter => [ \&from_chunks, 'mdecl_chunks', 'text'], },
 
130
      vdecl_chunks => { filter => [ sub { shift->[3] }, 'decl_inlines'], },
 
131
      vdecls => { filter => [ \&from_chunks, 'vdecl_chunks', 'text'], },
 
132
      vdecl_hash => { filter => [ \&vdecl_hash, 'vdecls', 'mdecls' ], },
 
133
      parsed_fdecls => { filter => [ \&do_declarations, 'fdecls', 
 
134
                                     'typedef_hash', 'keywords'], },
 
135
      keywords_rex => { filter => [ sub { my @k = keys %{ shift() };
 
136
                                          local $" = '|';
 
137
                                          my $r = "(?:@k)";
 
138
                                          eval 'qr/$r/' or $r   # Older Perls
 
139
                                        }, 'keywords'], },
 
140
    };
 
141
 
 
142
sub from_chunks {
 
143
  my $chunks = shift;
 
144
  my $txt = shift;
 
145
  my @out;
 
146
  my $i = 0;
 
147
  while ($i < @$chunks) {
 
148
    push @out, substr $txt, $chunks->[$i], $chunks->[ $i + 1 ] - $chunks->[$i];
 
149
    $i += 2;
 
150
  }
 
151
  \@out;
 
152
}
 
153
 
 
154
#sub process { request($recipes, @_) }
 
155
# Preloaded methods go here.
 
156
 
 
157
sub includes {
 
158
  my %seen;
 
159
  my $stream = new C::Preprocessed (@_)
 
160
    or die "Cannot open pipe from cppstdin: $!\n";
 
161
 
 
162
  while (<$stream>) {
 
163
    next unless m(^\s*\#\s*     # Leading hash
 
164
                  (line\s*)?    # 1: Optional line
 
165
                  ([0-9]+)\s*   # 2: Line number
 
166
                  (.*)          # 3: The rest
 
167
                 )x;
 
168
    my $include = $3;
 
169
    $include = $1 if $include =~ /"(.*)"/; # Filename may be in quotes
 
170
    $include =~ s,\\\\,/,g if $^O eq 'os2';
 
171
    $seen{$include}++ if $include ne "";
 
172
  }
 
173
  [keys %seen];
 
174
}
 
175
 
 
176
sub defines_maybe {
 
177
  my $file = shift;
 
178
  my ($mline,$line,%macros,%macrosargs,$sym,$args);
 
179
  open(C, $file) or die "Cannot open file $file: $!\n";
 
180
  while (not eof(C) and $line = <C>) {
 
181
    next unless 
 
182
      ( $line =~ s[
 
183
                   ^ \s* \# \s* # Start of directive
 
184
                   define \s+
 
185
                   (\w+)        # 1: symbol
 
186
                   (?:
 
187
                    \( (.*?) \s* \) # 2: Minimal match for arguments
 
188
                                    # in parenths (without trailing
 
189
                                    # spaces)
 
190
                   )?           # optional, no grouping
 
191
                   \s*          # rest is the definition
 
192
                   ([\s\S]*)    # 3: the rest
 
193
                  ][]x );
 
194
    ($sym, $args, $mline) = ($1, $2, $3);
 
195
    $mline .= <C> while not eof(C) and $mline =~ s/\\\n/\n/;
 
196
    chomp $mline;
 
197
    #print "sym: `$sym', args: `$args', mline: `$mline'\n";
 
198
    if (defined $args) {
 
199
      $macrosargs{$sym} = [ [split /\s*,\s*/, $args], $mline];
 
200
    } else {
 
201
      $macros{$sym} = $mline;
 
202
    }
 
203
  }
 
204
  close(C) or die "Cannot close file $file: $!\n";
 
205
  [\%macros, \%macrosargs];
 
206
}
 
207
 
 
208
sub defines_full {
 
209
  my $Cpp = $_[3];
 
210
  my ($mline,$line,%macros,%macrosargs,$sym,$args);
 
211
 
 
212
  # save the old cppflags and add the flag for only ouputting macro definitions
 
213
  my $old_cppstdin = $Cpp->{'cppstdin'};
 
214
  $Cpp->{'cppstdin'} = $old_cppstdin . " " . $ModPerl::CScan::MACROS_ONLY;
 
215
 
 
216
  my $stream = new C::Preprocessed (@_)
 
217
    or die "Cannot open pipe from cppstdin: $!\n";
 
218
 
 
219
  while (defined ($line = <$stream>)) {
 
220
    next unless 
 
221
      ( $line =~ s[
 
222
                   ^ \s* \# \s* # Start of directive
 
223
                   define \s+
 
224
                   (\w+)        # 1: symbol
 
225
                   (?:
 
226
                    \( (.*?) \s* \) # 2: Minimal match for arguments
 
227
                                    # in parenths (without trailing
 
228
                                    # spaces)
 
229
                   )?           # optional, no grouping
 
230
                   \s*          # rest is the definition
 
231
                   ([\s\S]*)    # 3: the rest
 
232
                  ][]x );
 
233
    ($sym, $args, $mline) = ($1, $2, $3);
 
234
    $mline .= <$stream> while ($mline =~ s/\\\n/\n/);
 
235
    chomp $mline;
 
236
#print STDERR "sym: `$sym', args: `$args', mline: `$mline'\n";
 
237
    if (defined $args) {
 
238
      $macrosargs{$sym} = [ [split /\s*,\s*/, $args], $mline];
 
239
    } else {
 
240
      $macros{$sym} = $mline;
 
241
    }
 
242
  }
 
243
  # restore the original cppflags
 
244
  $Cpp->{'cppstdin'} = $old_cppstdin;
 
245
  [\%macros, \%macrosargs];
 
246
}
 
247
 
 
248
sub typedef_chunks {            # Input is toplevel, output: starts and ends
 
249
  my $txt = shift;
 
250
  pos $txt = 0;
 
251
  my ($b, $e, @out);
 
252
  while ($txt =~ /\btypedef\b/g) {
 
253
    push @out, pos $txt;
 
254
    $txt =~ /(?=;)|\Z/g;
 
255
    push @out, pos $txt;
 
256
  }
 
257
  \@out;
 
258
}
 
259
 
 
260
sub struct_chunks {
 
261
  my $txt = shift;
 
262
  pos $txt = 0;
 
263
  my ($b, $e, @out);
 
264
  while ($txt =~ /\b(?=struct\s*(\w*\s*)?\{)/g) {
 
265
    push @out, pos $txt;
 
266
    $txt =~ /(?=;)|\Z/g;
 
267
    push @out, pos $txt;
 
268
  }
 
269
  \@out;
 
270
}
 
271
 
 
272
sub typedefs_whited {           # Input is sanitized text, and list of beg/end.
 
273
  my @lst = @{$_[1]};
 
274
  my @out;
 
275
  my ($b, $e);
 
276
  while ($b = shift @lst) {
 
277
    $e = shift @lst;
 
278
    push @out, whited_decl($_[2], substr $_[0], $b, $e - $b);
 
279
  }
 
280
  \@out;
 
281
}
 
282
 
 
283
sub structs_whited {
 
284
  my @lst = @{$_[1]};
 
285
  my @out;
 
286
  my ($b, $e, $in);
 
287
  while ($b = shift @lst) {
 
288
    $e = shift @lst;
 
289
    $in = substr $_[0], $b, $e - $b;
 
290
    $in =~ s/^(struct\s*(\w*\s*)?)(.*)$/$1 . " " x length($3)/es;
 
291
    push @out, $in;
 
292
  }
 
293
  \@out;
 
294
}
 
295
 
 
296
sub typedef_texts {
 
297
  my ($txt, $chunks) = (shift, shift);
 
298
  my ($b, $e, $in, @out);
 
299
  my @in = @$chunks;
 
300
  while (($b, $e) = splice @in, 0, 2) {
 
301
    $in = substr($txt, $b, $e - $b);
 
302
    # remove any remaining directives
 
303
    $in =~ s/^ ( \s* \# .* ( \\ $ \n .* )* ) / ' ' x length($1)/xgem;
 
304
    push @out, $in;
 
305
  }
 
306
  \@out;
 
307
}
 
308
 
 
309
sub typedef_hash {
 
310
  my ($typedefs, $whited) = (shift,shift);
 
311
  my %out;
 
312
 
 
313
 loop:
 
314
  for my $o (0..$#$typedefs) {
 
315
    my $wh = $whited->[$o];
 
316
    my $td = $typedefs->[$o];
 
317
#my $verb = $td =~ /apr_child_errfn_t/ ? 1 : 0;
 
318
#warn "$wh || $td\n" if $verb;
 
319
    if ($wh =~ /,/ or not $wh =~ /\w/) { # Hard case, guessimates ...
 
320
      # Determine whether the new thingies are inside parens
 
321
      $wh =~ /,/g;
 
322
      my $p = pos $wh;
 
323
      my ($s, $e);
 
324
      if (matchingbrace($wh)) { # Inside.  Easy part: just split on /,/...
 
325
        $e = pos($wh) - 1;
 
326
        $s = $e;
 
327
        my $d = 0;
 
328
        # Skip back
 
329
        while (--$s >= 0) {
 
330
          my $c = substr $wh, $s, 1;
 
331
          if ($c =~ /[\(\{\[]/) {
 
332
            $d--;
 
333
          } elsif ($c =~ /[\)\]\}]/) {
 
334
            $d++;
 
335
          }
 
336
          last if $d < 0;
 
337
        }
 
338
        if ($s < 0) {           # Should not happen
 
339
          warn("panic: could not match braces in\n\t$td\nwhited as\n\t$wh\n");
 
340
          next loop;
 
341
        }
 
342
        $s++;
 
343
      } else {                  # We are at toplevel
 
344
        # We need to skip back all the modifiers attached to the first thingy
 
345
        # Guesstimates: everything after the first '*' (inclusive)
 
346
        pos $wh = 0;
 
347
        $wh = /(?=\w)/g;
 
348
        my $ws = pos $wh;
 
349
        my $pre = substr $wh, 0, $ws;
 
350
        $s = $ws;
 
351
        $s = pos $pre if $pre =~ /(?=\*)/g;
 
352
        $e = length $wh;
 
353
      }
 
354
      # Now: need to split $td based on commas in $wh!
 
355
      # And need to split each chunk of $td based on word in the chunk of $wh!
 
356
      my $td_decls = substr($td, $s, $e - $s);
 
357
      my ($pre, $post) = (substr($td, 0, $s), substr($td, $e));
 
358
      my $wh_decls = substr($wh, $s, $e - $s);
 
359
      my @wh_decls = split /,/, $wh_decls;
 
360
      my $td_s = 0;
 
361
      my (@td_decl, @td_pre, @td_post, @td_word);
 
362
      for my $wh_d (@wh_decls) {
 
363
        my $td_d = substr $td, $td_s, length $wh_d;
 
364
        push @td_decl, $td_d;
 
365
        $wh_d =~ /(\w+)/g;
 
366
        push @td_word, $1;
 
367
        push @td_post, substr $td_d, pos($wh_d);
 
368
        push @td_pre,  substr $td_d, pos($wh_d) - length $1, length $1;
 
369
        $td_s += 1 + length $wh_d; # Skip over ','
 
370
      }
 
371
      for my $i (0..$#wh_decls) {
 
372
        my $p = "$td_post[$i]$post";
 
373
        $p = '' unless $p =~ /\S/;
 
374
        $out{$td_word[$i]} = ["$pre$td_pre[$i]", $p];
 
375
      }
 
376
    } elsif ($td =~ /\(\s* \*? \s* ([^)]+) \s* \) \s* \(.*\)/gxs){      # XXX: function pointer typedef
 
377
      $out{$1} = ['XXX: pre_foo', 'XXX: post_bar']; # XXX: not sure what to stuff here
 
378
      #warn "[$1] [$td]" if $verb;
 
379
    } else {                    # Only one thing defined...
 
380
      $wh =~ /(\w+)/g;
 
381
      my $e     = pos $wh;
 
382
      my $s     = $e - length $1;
 
383
      my $type  = $1;
 
384
      my $pre   = substr $td, 0, $s;
 
385
      my $post  = substr $td, $e, length($td) - $e;
 
386
      $post = '' unless $post =~ /\S/;
 
387
      $out{$type} = [$pre, $post];
 
388
    }
 
389
 
 
390
    #die if $verb;
 
391
 
 
392
  }
 
393
  \%out;
 
394
}
 
395
 
 
396
sub typedef_structs {
 
397
  my($typehash, $structs) = @_;
 
398
  my %structs;
 
399
  for (0 .. $#$structs) {
 
400
    my $in = $structs->[$_];
 
401
    my $key;
 
402
    next unless $in =~ /^struct\s*(\w+)/;
 
403
    next unless $in =~ s{^(struct\s*)(\w+)}{
 
404
      $key = "struct $2";
 
405
      $1 . " " x length($2)
 
406
    }e;
 
407
    my $name = parse_struct($in, \%structs);
 
408
    $structs{$key} = defined($name) ? $structs{$name} : undef;
 
409
  }
 
410
  while (my($key, $text) = each %$typehash) {
 
411
    my $name = parse_struct($text->[0], \%structs);
 
412
    $structs{$key} = defined($name) ? $structs{$name} : undef;
 
413
  }
 
414
  \%structs;
 
415
}
 
416
 
 
417
sub parse_struct {
 
418
  my($in, $structs) = @_;
 
419
  my($b, $e, $chunk, $vars, $struct, $structname);
 
420
  return "$1 $2" if $in =~ /
 
421
    ^ \s* (struct | union) \s+ (\w+) \s* $
 
422
  /x;
 
423
  ($structname, $in) = $in =~ /
 
424
    ^ \s* ( (?: struct | union ) (?: \s+ \w+ )? ) \s* { \s* (.*?) \s* } \s* $
 
425
  /gisx or return;
 
426
  $structname .= " _ANON" unless $structname =~ /\s/;
 
427
  $structname .= " 0" if exists $structs->{$structname};
 
428
  $structname =~ s/(\d+$)/$1 + 1/e while exists $structs->{$structname};
 
429
  $structname =~ s/\s+/ /g;
 
430
  $b = 0;
 
431
  while ($in =~ /(\{|;|$)/g) {
 
432
    matchingbrace($in), next if $1 eq '{';
 
433
    $e = pos($in);
 
434
    next if $b == $e;
 
435
    $chunk = substr($in, $b, $e - $b);
 
436
    $b = $e;
 
437
    if ($chunk =~ /\G\s*(struct|union|enum).*\}/gs) {
 
438
      my $term = pos $chunk;
 
439
      my $name = parse_struct(substr($chunk, 0, $term), $structs);
 
440
      $vars = parse_vars(join ' ', $name, substr $chunk, $term);
 
441
    } else {
 
442
      $vars = parse_vars($chunk);
 
443
    }
 
444
    push @$struct, @{$vars||[]};
 
445
  }
 
446
  $structs->{$structname} = $struct;
 
447
  $structname;
 
448
}
 
449
 
 
450
sub parse_vars {
 
451
  my $in = shift;
 
452
  my($vars, $type, $word, $id, $post, $func);
 
453
 
 
454
  while ($in =~ /\G\s*([\[;,(]|\*+|:\s*\d+|\S+?\b|$)\s*/gc) {
 
455
    $word = $1;
 
456
    if ($word eq ';' || $word eq '') {
 
457
      next unless defined $id;
 
458
      $type = 'int' unless defined $type;       # or is this an error?
 
459
      push @$vars, [ $type, $post, $id ];
 
460
      ($type, $post, $id, $func) = (undef, undef, undef);
 
461
    } elsif ($word eq ',') {
 
462
      warn "panic: expecting name before comma in '$in'\n" unless defined $id;
 
463
      $type = 'int' unless defined $type;       # or is this an error?
 
464
      push @$vars, [ $type, $post, $id ];
 
465
      $type =~ s/[ *]*$//;
 
466
      $id = undef;
 
467
    } elsif ($word eq '[') {
 
468
      warn "panic: expecting name before '[' in '$in'\n" unless defined $id;
 
469
      $type = 'int' unless defined $type;       # or is this an error?
 
470
      my $b = pos $in;
 
471
      matchingbrace($in);
 
472
      $post .= $word . substr $in, $b, pos($in) - $b;
 
473
    } elsif ($word eq '(') {
 
474
      # simple hack for function pointers
 
475
      $type = join ' ', grep defined, $type, $id if defined $id;
 
476
      $type = 'int' unless defined $type;
 
477
      if ($in =~ /\G\s*(\*[\s\*]*?)\s*(\w+)[\[\]\d\s]*(\)\s*\()/gc) {
 
478
        $type .= "($1";
 
479
        $id = $2;
 
480
        $post = $3;
 
481
        my $b = pos $in;
 
482
        matchingbrace($in);
 
483
        $post .= substr $in, $b, pos($in) - $b;
 
484
      } else {
 
485
        warn "panic: can't parse function pointer declaration in '$in'\n";
 
486
        return;
 
487
      }
 
488
    } elsif ($word =~ /^:/) {
 
489
      # bitfield
 
490
      $type = 'int' unless defined $type;
 
491
      $post .= $word;
 
492
    } else {
 
493
      if (defined $post) {
 
494
        if ($func) {
 
495
          $post .= $word;
 
496
        } else {
 
497
          warn "panic: not expecting '$word' after array bounds in '$in'\n";
 
498
        }
 
499
      } else {
 
500
        $type = join ' ', grep defined, $type, $id if defined $id;
 
501
        $id = $word;
 
502
      }
 
503
    }
 
504
  }
 
505
unless ($vars) {
 
506
  warn sprintf "failed on <%s> with type=<%s>, id=<%s>, post=<%s> at pos=%d\n",
 
507
    $in, $type, $id, $post, pos($in);
 
508
}
 
509
  $vars;
 
510
}
 
511
 
 
512
sub vdecl_hash {
 
513
  my($vdecls, $mdecls) = @_;
 
514
  my %vdecl_hash;
 
515
  for (@$vdecls, @$mdecls) {
 
516
    next if /[()]/;     # ignore functions, and function pointers
 
517
    my $copy = $_;
 
518
    next unless $copy =~ s/^\s*extern\s*//;
 
519
    my $vars = parse_vars($copy);
 
520
    $vdecl_hash{$_->[2]} = [ @$_[0, 1] ] for @$vars;
 
521
  }
 
522
  \%vdecl_hash;
 
523
}
 
524
 
 
525
# The output is the list of list of inline chunks and list of
 
526
# declaration chunks.
 
527
 
 
528
sub functions_in {              # The arg is text without type declarations.
 
529
  my $in = shift;               # remove_type_decl(top_level(sanitize($txt)));
 
530
  # What remains now consists of variable and function declarations,
 
531
  # and inline functions.
 
532
  $in =~ /(?=\S)/g;
 
533
  my ($b, $e, $b1, $e1, @inlines, @decls, @mdecls, @fdecls, @vdecls);
 
534
  $b = pos $in;
 
535
  my $chunk;
 
536
  while (defined($b) && $b != length $in) {
 
537
    $in =~ /;/g or pos $in = $b, $in =~ /.*\S|\Z/g ; # Or last non-space
 
538
    $e = pos $in;
 
539
    $chunk = substr $in, $b, $e - $b;
 
540
    # Now subdivide the chunk.
 
541
    # 
 
542
    # What we got is one chunk, probably finished by `;'. Whoever, it
 
543
    # may start with several inline functions.
 
544
    #
 
545
    # Note that inline functions contain ( ) { } in the stripped version.
 
546
    $b1 = 0;
 
547
    while ($chunk =~ /\(\s*\)\s*\{\s*\}/g) {
 
548
      $e1 = pos $chunk;
 
549
      push @inlines, $b + $b1, $b + $e1;
 
550
      $chunk =~ /(?=\S)/g;
 
551
      $b1 = pos $chunk; 
 
552
      $b1 = length $chunk, last unless defined $b1;
 
553
    }
 
554
    if ($e - $b - $b1 > 0) {
 
555
      my($isvar, $isfunc) = (1, 1);
 
556
      substr ($chunk, 0, $b1) = '';
 
557
      if ($chunk =~ /,/) {      # Contains multiple declarations.
 
558
        push @mdecls, $b + $b1, $e;
 
559
      } else  {                 # Non-multiple.
 
560
        # Since leading \s* is not optimized, this is quadratic!
 
561
        $chunk =~ s{
 
562
                     ( ( const | __const
 
563
                         | __attribute__ \s* \( \s* \)
 
564
                       ) \s* )* ( ; \s* )? \Z # Strip from the end
 
565
                   }()x;
 
566
        $chunk =~ s/\s*\Z//;
 
567
        if ($chunk =~ /\)\Z/) { # Function declaration ends on ")"!
 
568
          if ($chunk !~ m{ 
 
569
                          \( .* \( # Multiple parenths
 
570
                         }x
 
571
              and $chunk =~ / \w \s* \( /x) { # Most probably pointer to a function?
 
572
            $isvar = 0;
 
573
          }
 
574
        } elsif ($chunk =~ /
 
575
          ^ \s* (enum|struct|union|class) \s+ \w+ \s* $
 
576
        /x) {
 
577
          $isvar = $isfunc = 0;
 
578
        }
 
579
        if ($isvar)  {  # Heuristically variable
 
580
          push @vdecls, $b + $b1, $e;
 
581
        } elsif ($isfunc) {
 
582
          push @fdecls, $b + $b1, $e;
 
583
        }
 
584
      }
 
585
      push @decls, $b + $b1, $e if $isvar || $isfunc;
 
586
    }
 
587
    $in =~ /\G\s*/g ;
 
588
    $b = pos $in;
 
589
  }
 
590
  [\@inlines, \@decls, \@mdecls, \@vdecls, \@fdecls];
 
591
}
 
592
 
 
593
# XXXX This is heuristical in many respects...
 
594
# Recipe: remove all struct-ish chunks.  Remove all array specifiers.
 
595
# Remove GCC attribute specifiers.
 
596
# What remains may contain function's arguments, old types, and newly
 
597
# defined types.
 
598
# Remove function arguments using heuristics methods.
 
599
# Now out of several words in a row the last one is a newly defined type.
 
600
 
 
601
sub whited_decl {               # Input is sanitized.
 
602
  my $keywords_rex = shift;
 
603
  my $in = shift;               # Text of a declaration
 
604
 
 
605
  #typedef ret_type*(*func) -> typedef ret_type* (*func)
 
606
  $in =~ s/\*\(\*/* \(*/;
 
607
 
 
608
  my $rest  = $in;
 
609
  my $out  = $in;               # Whited out $in
 
610
 
 
611
  # Remove all the structs
 
612
  while ($out =~ /(\b(struct|union|class|enum)(\s+\w+)?\s*\{)/g) {
 
613
    my $pos_start = pos($out) - length $1;
 
614
 
 
615
    matchingbrace($out);
 
616
    my $pos_end = pos $out;
 
617
    substr($out, $pos_start, $pos_end - $pos_start) =
 
618
        ' ' x ($pos_end - $pos_start);
 
619
    pos $out = $pos_end;
 
620
  }
 
621
 
 
622
  # Deal with glibc's wierd ass __attribute__ tag.  Just dump it.
 
623
  # Maaaybe this should check to see if you're using GCC, but I don't
 
624
  # think so since glibc is nice enough to do that for you.  [MGS]
 
625
  while ( $out =~ m/(\b(__attribute__|attribute)\s*\((?=\s*\())/g ) {
 
626
      my $att_pos_start = pos($out) - length($1);
 
627
 
 
628
      # Need to figure out where ((..)) ends.
 
629
      matchingbrace($out);
 
630
      my $att_pos_end = pos $out;
 
631
 
 
632
      # Remove the __attribute__ tag.
 
633
      substr($out, $att_pos_start, $att_pos_end - $att_pos_start) =
 
634
        ' ' x ($att_pos_end - $att_pos_start);
 
635
      pos $out = $att_pos_end;
 
636
  }
 
637
 
 
638
  # Remove arguments of functions (heuristics only).
 
639
  # These things (start) arglist of a declared function:
 
640
  # paren word comma
 
641
  # paren word space non-paren
 
642
  # paren keyword paren
 
643
  # start a list of arguments. (May be "cdecl *myfunc"?) XXXXX ?????
 
644
  while ( $out =~ /(\(\s*(\w+(,|\s*[^\)\s])|$keywords_rex\s*\)))/g ) {
 
645
    my $pos_start = pos($out) - length($1);
 
646
    pos $out = $pos_start + 1;
 
647
    matchingbrace($out);
 
648
    substr ($out, $pos_start + 1, pos($out) - 2 - $pos_start)
 
649
      = ' ' x (pos($out) - 2 - $pos_start);
 
650
  }
 
651
  # Remove array specifiers
 
652
  $out =~ s/(\[[\w\s\+]*\])/ ' ' x length $1 /ge;
 
653
  my $tout = $out;
 
654
  # Several words in a row cannot be new typedefs, but the last one.
 
655
  $out =~ s/((\w+\**\s+)+(?=[^\s,;\[\{\)]))/ ' ' x length $1 /ge;
 
656
  unless ($out =~ /\w/) {
 
657
    # Probably a function-type declaration: typedef int f(int);
 
658
    # Redo scan leaving the last word of the first group of words:
 
659
    $tout =~ /(\w+\s+)*(\w+)\s*\(/g;
 
660
    $out = ' ' x (pos($tout) - length $2)
 
661
      . $2 . ' ' x (length($tout) - pos($tout));
 
662
    # warn "function typedef\n\t'$in'\nwhited-out as\n\t'$out'\n";
 
663
  }
 
664
  warn "panic: length mismatch\n\t'$in'\nwhited-out as\n\t'$out'\n"
 
665
    if length($in) != length $out;
 
666
  # Sanity check
 
667
  warn "panic: multiple types without intervening comma in\n\t$in\nwhited-out as\n\t$out\n"
 
668
    if $out =~ /\w[^\w,]+\w/;
 
669
  warn "panic: no types found in\n\t$in\nwhited-out as\n\t$out\n"
 
670
    unless $out =~ /\w/;
 
671
  $out
 
672
}
 
673
 
 
674
sub matchingbrace {
 
675
  # pos($_[0]) is after the opening brace now
 
676
  my $n = 0;
 
677
  while ($_[0] =~ /([\{\[\(])|([\]\)\}])/g) {
 
678
    $1 ? $n++ : $n-- ;
 
679
    return 1 if $n < 0;
 
680
  }
 
681
  # pos($_[0]) is after the closing brace now
 
682
  return;                               # false
 
683
}
 
684
 
 
685
sub remove_Comments_no_Strings { # We expect that no strings are around
 
686
    my $in = shift;
 
687
    $in =~ s,/(/.*|\*[\s\S]*?\*/),,g ; # C and C++
 
688
    die "Unfinished comment" if $in =~ m,/\*, ;
 
689
    $in;
 
690
}
 
691
 
 
692
sub sanitize {          # We expect that no strings are around
 
693
    my $in = shift;
 
694
    # C and C++, strings and characters
 
695
    $in =~ s{ / (
 
696
                 / .*                   # C++ style
 
697
                 |
 
698
                 \* [\s\S]*? \*/        # C style
 
699
                )                       # (1)
 
700
             | '((?:[^\\\']|\\.)+)'     # (2) Character constants
 
701
             | "((?:[^\\\"]|\\.)*)"     # (3) Strings
 
702
             | ( ^ \s* \# .*            # (4) Preprocessor
 
703
                 ( \\ $ \n .* )* )      # and continuation lines
 
704
            } {
 
705
              # We want to preserve the length, so that one may go back
 
706
              defined $1 ? ' ' x (1 + length $1) :
 
707
                defined $4 ? ' ' x length $4 :
 
708
                  defined $2 ? "'" . ' ' x length($2) . "'" :
 
709
                    defined $3 ? '"' . ' ' x length($3) . '"' : '???'
 
710
            }xgem ;
 
711
    die "Unfinished comment" if $in =~ m{ /\* }x;
 
712
    $in;
 
713
}
 
714
 
 
715
sub top_level {                 # We expect argument is sanitized
 
716
  # Note that this may remove the variable in declaration: int (*func)();
 
717
  my $in = shift;
 
718
  my $start;
 
719
  my $out = $in;
 
720
  while ($in =~ /[\[\{\(]/g ) {
 
721
    $start = pos $in;
 
722
    matchingbrace($in);
 
723
    substr($out, $start, pos($in) - 1 - $start) 
 
724
      = ' ' x (pos($in) - 1 - $start);
 
725
  }
 
726
  $out;
 
727
}
 
728
 
 
729
sub remove_type_decl {          # We suppose that the arg is top-level only.
 
730
  my $in = shift;
 
731
  $in =~ s/(\b__extension__)(\s+typedef\b)/(' ' x length $1) . $2/gse;
 
732
  $in =~ s/(\btypedef\b.*?;)/' ' x length $1/gse;
 
733
  # The following form may appear only in the declaration of the type itself:
 
734
  $in =~ 
 
735
    s/(\b(enum|struct|union|class)\b[\s\w]*\{\s*\}\s*;)/' ' x length $1/gse;
 
736
  $in;
 
737
}
 
738
 
 
739
sub new {
 
740
  my $class = shift;
 
741
  my $out = SUPER::new $class $recipes;
 
742
  $out->set(@_);
 
743
  $out;
 
744
}
 
745
 
 
746
sub do_declarations {
 
747
  my @d = map do_declaration($_, $_[1], $_[2]), @{ $_[0] };
 
748
  \@d;
 
749
}
 
750
 
 
751
# Forth argument: if defined, there maybe no identifier. Generate one
 
752
# basing on this argument.
 
753
 
 
754
sub do_declaration {
 
755
  my ($decl, $typedefs, $keywords, $argnum) = @_;
 
756
  $decl =~ s/;?\s*$//;
 
757
 
 
758
  my ($type, $typepre, $typepost, $ident, $args, $w, $pos, $repeater);
 
759
  $decl =~ s/[\r\n]\s*/ /g;
 
760
#warn "DECLAR [$decl][$argnum]\n";
 
761
  $decl =~ s/^\s*__extension__\b\s*//;
 
762
  $decl =~ s/^\s*extern\b\s*//;
 
763
  $decl =~ s/^\s*__inline\b\s*//;
 
764
  $pos = 0;
 
765
  while ($decl =~ /(\w+)/g and ($typedefs->{$1} or $keywords->{$1})) {
 
766
    $w = $1;
 
767
    if ($w =~ /^(struct|class|enum|union)$/) {
 
768
      $decl =~ /\G\s+\w+/g or die "`$w' is not followed by word in `$decl'";
 
769
    }
 
770
    $pos = pos $decl;
 
771
  }
 
772
#warn "pos: $pos\n";
 
773
  pos $decl = $pos;
 
774
  $decl =~ /\G[\s*]*\*/g or pos $decl = $pos;
 
775
  $type = substr $decl, 0, pos $decl;
 
776
  $decl =~ /\G\s*/g or pos $decl = length $type; # ????
 
777
  $pos = pos $decl;
 
778
#warn "pos: $pos\n";
 
779
  if (defined $argnum) {
 
780
    if ($decl =~ /\G(\w+)((\s*\[[^][]*\])*)/g) { # The best we can do with [2]
 
781
      $ident = $1;
 
782
      $repeater = $2;
 
783
      $pos = pos $decl;
 
784
    } else {
 
785
      pos $decl = $pos = length $decl;
 
786
      $type = $decl;
 
787
      $ident = "arg$argnum";
 
788
    }
 
789
  } else {
 
790
    die "Cannot process declaration `$decl' without an identifier"
 
791
      unless $decl =~ /\G(\w+)/g;
 
792
    $ident = $1;
 
793
    $pos = pos $decl;
 
794
  }
 
795
#warn "pos: $pos\n";
 
796
  $decl =~ /\G\s*/g or pos $decl = $pos;
 
797
  $pos = pos $decl;
 
798
#my $st = length $decl;
 
799
#warn substr($decl, 0, $pos), "\n";
 
800
#warn "pos: $pos $st\n";
 
801
#warn "DECLAR [$decl][$argnum]\n";
 
802
  if (pos $decl != length $decl) {
 
803
    pos $decl = $pos;
 
804
    die "Expecting parenth after identifier in `$decl'\nafter `",
 
805
      substr($decl, 0, $pos), "'"
 
806
      unless $decl =~ /\G\(/g;
 
807
    my $argstring = substr($decl, pos($decl) - length $decl);
 
808
    matchingbrace($argstring) or die "Cannot find matching parenth in `$decl'";
 
809
    $argstring = substr($argstring, 0, pos($argstring) - 1);
 
810
    $argstring =~ s/ ^ ( \s* void )? \s* $ //x;
 
811
    $args = [];
 
812
    my @args;
 
813
    if ($argstring ne '') {
 
814
      my $top = top_level $argstring;
 
815
      my $p = 0;
 
816
      my $arg;
 
817
      while ($top =~ /,/g) {
 
818
        $arg = substr($argstring, $p, pos($top) - 1 - $p);
 
819
        $arg =~ s/^\s+|\s+$//gs;
 
820
        push @args, $arg;
 
821
        $p = pos $top;
 
822
      }
 
823
      $arg = substr $argstring, $p;
 
824
      $arg =~ s/^\s+|\s+$//gs;
 
825
      push @args, $arg;
 
826
    }
 
827
    my $i = 0;
 
828
    for (@args) {
 
829
      push @$args, do_declaration1($_, $typedefs, $keywords, $i++);
 
830
    }
 
831
  }
 
832
  [$type, $ident, $args, $decl, $repeater];
 
833
}
 
834
 
 
835
sub do_declaration1 {
 
836
  my ($decl, $typedefs, $keywords, $argnum) = @_;
 
837
  $decl =~ s/;?\s*$//;
 
838
#warn "DECLARO [$decl][$argnum]\n";
 
839
  my ($type, $typepre, $typepost, $ident, $args, $w, $pos, $repeater);
 
840
  $pos = 0;
 
841
  while ($decl =~ /(\w+)/g and ($typedefs->{$1} or $keywords->{$1})) {
 
842
    $w = $1;
 
843
    if ($w =~ /^(struct|class|enum|union)$/) {
 
844
      $decl =~ /\G\s+\w+/g or die "`$w' is not followed by word in `$decl'";
 
845
    }
 
846
    $pos = pos $decl;
 
847
  }
 
848
#warn "POS: $pos\n";
 
849
  pos $decl = $pos;
 
850
  $decl =~ /\G[\s*]*\*/g or pos $decl = $pos;
 
851
  $type = substr $decl, 0, pos $decl;
 
852
  $decl =~ /\G\s*/g or pos $decl = length $type; # ????
 
853
  $pos = pos $decl;
 
854
  if (defined $argnum) {
 
855
    if ($decl =~ /\G(\w+)((\s*\[[^][]*\])*)/g) { # The best we can do with [2]
 
856
      $ident = $1;
 
857
      $repeater = $2;
 
858
      $pos = pos $decl;
 
859
    } else {
 
860
      pos $decl = $pos = length $decl;
 
861
      $type = $decl;
 
862
      $ident = "arg$argnum";
 
863
    }
 
864
  } else {
 
865
    die "Cannot process declaration `$decl' without an identifier" 
 
866
      unless $decl =~ /\G(\w+)/g;
 
867
    $ident = $1;
 
868
    $pos = pos $decl;
 
869
  }
 
870
  $decl =~ /\G\s*/g or pos $decl = $pos;
 
871
  $pos = pos $decl;
 
872
#warn "DECLAR1 [$decl][$argnum]\n";
 
873
#my $st = length $decl;
 
874
#warn substr($decl, 0, $pos), "\n";
 
875
#warn "pos: $pos $st\n";
 
876
  if (pos $decl != length $decl) {
 
877
    pos $decl = $pos;
 
878
    die "Expecting parenth after identifier in `$decl'\nafter `",
 
879
      substr($decl, 0, $pos), "'"
 
880
      unless $decl =~ /\G\(/g;
 
881
    my $argstring = substr($decl, pos($decl) - length $decl);
 
882
    matchingbrace($argstring) or die "Cannot find matching parenth in `$decl'";
 
883
    $argstring = substr($argstring, 0, pos($argstring) - 1);
 
884
    $argstring =~ s/ ^ ( \s* void )? \s* $ //x;
 
885
    $args = [];
 
886
    my @args;
 
887
    if ($argstring ne '') {
 
888
      my $top = top_level $argstring;
 
889
      my $p = 0;
 
890
      my $arg;
 
891
      while ($top =~ /,/g) {
 
892
        $arg = substr($argstring, $p, pos($top) - 1 - $p);
 
893
        $arg =~ s/^\s+|\s+$//gs;
 
894
        push @args, $arg;
 
895
        $p = pos $top;
 
896
      }
 
897
      $arg = substr $argstring, $p;
 
898
      $arg =~ s/^\s+|\s+$//gs;
 
899
      push @args, $arg;
 
900
    }
 
901
    my $i = 0;
 
902
    for (@args) {
 
903
      push @$args, do_declaration1($_, $typedefs, $keywords, $i++);
 
904
    }
 
905
  }
 
906
  [$type, $ident, $args, $decl, $repeater];
 
907
}
 
908
 
 
909
############################################################
 
910
 
 
911
package C::Preprocessed;
 
912
use Symbol;
 
913
use File::Basename;
 
914
use Config;
 
915
 
 
916
sub new {
 
917
    die "usage: C::Preprocessed->new(filename[, defines[, includes[, cpp]]])" 
 
918
      if @_ < 2 or @_ > 5;
 
919
    my ($class, $filename, $Defines, $Includes, $Cpp) 
 
920
      = (shift, shift, shift, shift, shift);
 
921
    $Cpp ||= \%Config::Config;
 
922
    my $filedir = dirname $filename || '.';
 
923
    $Includes ||= [$filedir, '/usr/local/include', '.'];
 
924
    my $addincludes = "";
 
925
    $addincludes = "-I" . join(" -I", @$Includes)
 
926
      if defined $Includes and @$Includes;
 
927
    my($sym) = gensym;
 
928
    my $cmd = "echo '\#include \"$filename\"' | $Cpp->{cppstdin} $Defines $addincludes $Cpp->{cppflags} $Cpp->{cppminus} |";
 
929
    #my $cmd = "$Cpp->{cppstdin} $Defines $addincludes $Cpp->{cppflags} $Cpp->{cppminus} < $filename |";
 
930
    #my $cmd = "echo '\#include <$filename>' | $Cpp->{cppstdin} $Defines $addincludes $Cpp->{cppflags} $Cpp->{cppminus} |";
 
931
 
 
932
    (open($sym, $cmd) or die "Cannot open pipe from `$cmd': $!")
 
933
      and bless $sym => $class;
 
934
}
 
935
 
 
936
sub text {
 
937
  my $class = shift;
 
938
  my $filter = shift;
 
939
  if (defined $filter) {
 
940
    return text_only_from($class, $filter, @_);
 
941
  }
 
942
  my $stream = $class->new(@_);
 
943
  my $oh = select $stream;
 
944
  local $/;
 
945
  select $oh;
 
946
  <$stream>;
 
947
}
 
948
 
 
949
sub text_only_from {
 
950
  my $class = shift;
 
951
  my $from = shift || die "Expecting argument in `text_only_from'";
 
952
  my $stream = $class->new(@_);
 
953
  my $on = $from eq $_[0];
 
954
  my $eqregexp = $on ? '\"\"|' : '';
 
955
  my @out;
 
956
  while (<$stream>) {
 
957
    #print;
 
958
 
 
959
    $on = /$eqregexp[\"\/]\Q$from\"/ if /^\#/;
 
960
    push @out, $_ if $on;
 
961
  }
 
962
  join '', @out;
 
963
}
 
964
 
 
965
sub DESTROY {
 
966
  close($_[0]) 
 
967
    or die "Cannot close pipe from `$Config::Config{cppstdin}': err $?, $!\n";
 
968
}
 
969
 
 
970
# Autoload methods go after __END__, and are processed by the autosplit program.
 
971
# Return to the principal package.
 
972
package ModPerl::CScan;
 
973
 
 
974
1;
 
975
__END__
 
976
 
 
977
=head1 NAME
 
978
 
 
979
ModPerl::CScan - scan C language files for easily recognized constructs.
 
980
 
 
981
=head1 SYNOPSIS
 
982
 
 
983
=head1 DESCRIPTION
 
984
 
 
985
See the C<C::Scan> manpage. This package is just a fork to fix certain
 
986
things that didn't work in the original C<C::Scan>, which is not
 
987
maintained any longer. These fixes required to make it work with the
 
988
Apache 2 source code.
 
989
 
 
990
=cut