1 ################################################################################
3 # ppptools.pl -- various utility functions
5 ################################################################################
9 # $Date: 2004/08/13 12:50:05 +0200 $
11 ################################################################################
13 # Version 3.x, Copyright (C) 2004, Marcus Holland-Moritz.
14 # Version 2.x, Copyright (C) 2001, Paul Marquess.
15 # Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
17 # This program is free software; you can redistribute it and/or
18 # modify it under the same terms as Perl itself.
20 ################################################################################
24 my $dir = shift || 'parts/todo';
29 for $todo (glob "$dir/*") {
30 open TODO, $todo or die "cannot open $todo: $!\n";
38 /^\w+$/ or die "invalid identifier: $_\n";
39 exists $todo{$_} and die "duplicate identifier: $_ ($todo{$_} <=> $perl)\n";
51 my $section = 'implementation';
52 my $vsec = join '|', qw( provides dontwarn implementation
53 xsubs xsinit xsmisc xshead xsboot tests );
57 open F, $file or die "$file: $!\n";
60 if (/^=($vsec)(?:\s+(.*))?/) {
64 $options{$section} = eval "{ $opt }";
65 $@ and die "Invalid options ($opt) in section $section of $file: $@\n";
69 push @{$data{$section}}, $_;
75 shift @v while @v && $v[0] =~ /^\s*$/;
76 pop @v while @v && $v[-1] =~ /^\s*$/;
77 $data{$_} = join '', @v;
80 unless (exists $data{provides}) {
81 $data{provides} = ($file =~ /(\w+)$/)[0];
83 $data{provides} = [$data{provides} =~ /(\S+)/g];
85 if (exists $data{dontwarn}) {
86 $data{dontwarn} = [$data{dontwarn} =~ /(\S+)/g];
92 if (exists $data{tests} && (!exists $data{implementation} || $data{implementation} !~ /\S/)) {
93 $data{implementation} = '';
96 $data{implementation} =~ /\S/ or die "Empty implementation in $file\n";
100 for $p (@{$data{provides}}) {
101 if ($p =~ m#^/.*/\w*$#) {
102 my @tmp = eval "\$data{implementation} =~ ${p}gm";
103 $@ and die "invalid regex $p in $file\n";
104 @tmp or warn "no matches for regex $p in $file\n";
105 push @prov, do { my %h; grep !$h{$_}++, @tmp };
107 elsif ($p eq '__UNDEFINED__') {
108 my @tmp = $data{implementation} =~ /^\s*__UNDEFINED__[^\r\n\S]+(\w+)/gm;
109 @tmp or warn "no __UNDEFINED__ macros in $file\n";
118 if ($data{implementation} !~ /\b\Q$_\E\b/) {
119 warn "$file claims to provide $_, but doesn't seem to do so\n";
123 # scan for prototypes
124 my($proto) = $data{implementation} =~ /
125 ( ^ (?:[\w*]|[^\S\r\n])+
137 exists $proto{$_} and warn "$file: duplicate prototype for $_\n";
142 $data{provides} = \@prov;
143 $data{prototypes} = \%proto;
144 $data{OPTIONS} = \%options;
146 my %prov = map { ($_ => 1) } @prov;
147 my %dontwarn = exists $data{dontwarn} ? map { ($_ => 1) } @{$data{dontwarn}} : ();
148 my @maybeprov = do { my %h;
150 my($nop) = /^Perl_(.*)/;
151 not exists $prov{$_} ||
152 exists $dontwarn{$_} ||
153 (defined $nop && exists $prov{$nop} ) ||
154 (defined $nop && exists $dontwarn{$nop}) ||
157 $data{implementation} =~ /^\s*#\s*define\s+(\w+)/g };
160 warn "$file seems to provide these macros, but doesn't list them:\n "
161 . join("\n ", @maybeprov) . "\n";
167 sub compare_prototypes
187 push @c, map "!($_)", @{$p->{pre}};
188 defined $p->{cur} and push @c, "($p->{cur})";
198 $in eq '...' and return ($in);
205 s/\b(?:auto|const|extern|inline|register|static|volatile|restrict)\b//g;
208 if( /^\b(?:struct|union|enum)\s+\w+(?:\s+(\w+))?$/ ) {
209 defined $1 and $id = $1;
212 if( s/\b(?:char|double|float|int|long|short|signed|unsigned|void)\b//g ) {
213 /^\s*(\w+)\s*$/ and $id = $1;
216 /^\s*\w+\s+(\w+)\s*$/ and $id = $1;
222 defined $id and s/\b$id\b//;
224 # these don't matter at all
225 s/\b(?:auto|extern|inline|register|static|volatile|restrict)\b//g;
227 s/(?=<\*)\s+(?=\*)//g;
228 s/\s*(\*+)\s*/ $1 /g;
244 open FILE, $file or die "$file: $!\n";
247 while (defined($line = <FILE>)) {
248 while ($line =~ /\\$/ && defined($l = <FILE>)) {
252 next if $line =~ /^\s*:/;
253 $line =~ s/^\s+|\s+$//gs;
254 my($dir, $args) = ($line =~ /^\s*#\s*(\w+)(?:\s*(.*?)\s*)?$/);
255 if (defined $dir and defined $args) {
257 /^ifdef$/ and do { push @pps, { pre => [], cur => "defined($args)" } ; last };
258 /^ifndef$/ and do { push @pps, { pre => [], cur => "!defined($args)" } ; last };
259 /^if$/ and do { push @pps, { pre => [], cur => $args } ; last };
260 /^elif$/ and do { push @{$pps[-1]{pre}}, $pps[-1]{cur}; $pps[-1]{cur} = $args; last };
261 /^else$/ and do { push @{$pps[-1]{pre}}, $pps[-1]{cur}; $pps[-1]{cur} = undef; last };
262 /^endif$/ and do { pop @pps ; last };
263 /^include$/ and last;
266 warn "unhandled preprocessor directive: $dir\n";
270 my @e = split /\s*\|\s*/, $line;
272 my($flags, $ret, $name, @args) = @e;
276 ($ret) = trim_arg($ret);
279 flags => { map { $_, 1 } $flags =~ /./g },
282 cond => ppcond(\@pps),
297 my @args = map { "@$_" } @{$f->{args}};
299 my $pTHX_ = exists $f->{flags}{n} ? "" : "pTHX_ ";
300 $proto = "$f->{ret} $f->{name}" . "($pTHX_" . join(', ', @args) . ')';
309 my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
314 if ($r < 5 || ($r == 5 && $v < 6)) {
316 die "invalid version '$ver'\n";
320 $ver = sprintf "%d.%03d", $r, $v;
321 $s > 0 and $ver .= sprintf "_%02d", $s;
326 return sprintf "%d.%d.%d", $r, $v, $s;
333 if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) {
336 elsif ($ver !~ /^\d+\.[\d_]+$/) {
337 die "cannot parse version '$ver'\n";
343 my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
348 if ($r < 5 || ($r == 5 && $v < 6)) {
350 die "cannot parse version '$ver'\n";