1 ################################################################################
3 # ppptools.pl -- various utility functions
5 ################################################################################
7 # Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
8 # Version 2.x, Copyright (C) 2001, Paul Marquess.
9 # Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
11 # This program is free software; you can redistribute it and/or
12 # modify it under the same terms as Perl itself.
14 ################################################################################
18 eval { require File::Spec };
19 return $@ ? join('/', @_) : File::Spec->catfile(@_);
27 opendir DIR, $dir or die "cannot open directory $dir: $!\n";
28 my @files = grep { !-d && !/^\./ } readdir DIR; # no dirs or hidden files
31 return map { cat_file($dir, $_) } sort @files;
36 my $dir = shift || 'parts/todo';
41 for $todo (all_files_in_dir($dir)) {
42 open TODO, $todo or die "cannot open $todo: $!\n";
50 /^\w+$/ or die "invalid identifier: $_\n";
51 exists $todo{$_} and die "duplicate identifier: $_ ($todo{$_} <=> $perl)\n";
63 my($r, $v, $s) = parse_version($ver);
64 $r == 5 or die "only Perl revision 5 is supported\n";
65 my $bcdver = sprintf "0x%d%03d%03d", $r, $v, $s;
66 return "(PERL_BCDVERSION $op $bcdver)";
72 my $section = 'implementation';
73 my $vsec = join '|', qw( provides dontwarn implementation
74 xsubs xsinit xsmisc xshead xsboot tests );
78 open F, $file or die "$file: $!\n";
80 /[ \t]+$/ and warn "$file:$.: warning: trailing whitespace\n";
81 if ($section eq 'implementation') {
82 m!//! && !m!(?:=~|s/).*//! && !m!(?:ht|f)tp(?:s)://!
83 and warn "$file:$.: warning: potential C++ comment\n";
86 if (/^=($vsec)(?:\s+(.*))?/) {
90 $options{$section} = eval "{ $opt }";
91 $@ and die "$file:$.: invalid options ($opt) in section $section: $@\n";
95 push @{$data{$section}}, $_;
100 my @v = @{$data{$_}};
101 shift @v while @v && $v[0] =~ /^\s*$/;
102 pop @v while @v && $v[-1] =~ /^\s*$/;
103 $data{$_} = join '', @v;
106 unless (exists $data{provides}) {
107 $data{provides} = ($file =~ /(\w+)\.?$/)[0];
109 $data{provides} = [$data{provides} =~ /(\S+)/g];
111 if (exists $data{dontwarn}) {
112 $data{dontwarn} = [$data{dontwarn} =~ /(\S+)/g];
118 if (exists $data{tests} && (!exists $data{implementation} || $data{implementation} !~ /\S/)) {
119 $data{implementation} = '';
122 $data{implementation} =~ /\S/ or die "Empty implementation in $file\n";
126 for $p (@{$data{provides}}) {
127 if ($p =~ m#^/.*/\w*$#) {
128 my @tmp = eval "\$data{implementation} =~ ${p}gm";
129 $@ and die "invalid regex $p in $file\n";
130 @tmp or warn "no matches for regex $p in $file\n";
131 push @prov, do { my %h; grep !$h{$_}++, @tmp };
133 elsif ($p eq '__UNDEFINED__') {
134 my @tmp = $data{implementation} =~ /^\s*__UNDEFINED__[^\r\n\S]+(\w+)/gm;
135 @tmp or warn "no __UNDEFINED__ macros in $file\n";
144 if ($data{implementation} !~ /\b\Q$_\E\b/) {
145 warn "$file claims to provide $_, but doesn't seem to do so\n";
149 # scan for prototypes
150 my($proto) = $data{implementation} =~ /
151 ( ^ (?:[\w*]|[^\S\r\n])+
163 exists $proto{$_} and warn "$file: duplicate prototype for $_\n";
168 for $section (qw( implementation xsubs xsinit xsmisc xshead xsboot )) {
169 if (exists $data{$section}) {
170 $data{$section} =~ s/\{\s*version\s*(<|>|==|!=|>=|<=)\s*([\d._]+)\s*\}/expand_version($1, $2)/gei;
174 $data{provides} = \@prov;
175 $data{prototypes} = \%proto;
176 $data{OPTIONS} = \%options;
178 my %prov = map { ($_ => 1) } @prov;
179 my %dontwarn = exists $data{dontwarn} ? map { ($_ => 1) } @{$data{dontwarn}} : ();
180 my @maybeprov = do { my %h;
182 my($nop) = /^Perl_(.*)/;
183 not exists $prov{$_} ||
184 exists $dontwarn{$_} ||
186 (defined $nop && exists $prov{$nop} ) ||
187 (defined $nop && exists $dontwarn{$nop}) ||
190 $data{implementation} =~ /^\s*#\s*define\s+(\w+)/gm };
193 warn "$file seems to provide these macros, but doesn't list them:\n "
194 . join("\n ", @maybeprov) . "\n";
200 sub compare_prototypes
220 push @c, map "!($_)", @{$p->{pre}};
221 defined $p->{cur} and push @c, "($p->{cur})";
230 my $remove = join '|', qw( NN NULLOK VOL );
232 $in eq '...' and return ($in);
239 s/\b(?:auto|const|extern|inline|register|static|volatile|restrict)\b//g;
243 if( /^\b(?:struct|union|enum)\s+\w+(?:\s+(\w+))?$/ ) {
244 defined $1 and $id = $1;
247 if( s/\b(?:char|double|float|int|long|short|signed|unsigned|void)\b//g ) {
248 /^\s*(\w+)\s*$/ and $id = $1;
251 /^\s*\w+\s+(\w+)\s*$/ and $id = $1;
257 defined $id and s/\b$id\b//;
259 # these don't matter at all
260 s/\b(?:auto|extern|inline|register|static|volatile|restrict)\b//g;
263 s/(?=<\*)\s+(?=\*)//g;
264 s/\s*(\*+)\s*/ $1 /g;
280 open FILE, $file or die "$file: $!\n";
283 while (defined($line = <FILE>)) {
284 while ($line =~ /\\$/ && defined($l = <FILE>)) {
288 next if $line =~ /^\s*:/;
289 $line =~ s/^\s+|\s+$//gs;
290 my($dir, $args) = ($line =~ /^\s*#\s*(\w+)(?:\s*(.*?)\s*)?$/);
291 if (defined $dir and defined $args) {
293 /^ifdef$/ and do { push @pps, { pre => [], cur => "defined($args)" } ; last };
294 /^ifndef$/ and do { push @pps, { pre => [], cur => "!defined($args)" } ; last };
295 /^if$/ and do { push @pps, { pre => [], cur => $args } ; last };
296 /^elif$/ and do { push @{$pps[-1]{pre}}, $pps[-1]{cur}; $pps[-1]{cur} = $args; last };
297 /^else$/ and do { push @{$pps[-1]{pre}}, $pps[-1]{cur}; $pps[-1]{cur} = undef; last };
298 /^endif$/ and do { pop @pps ; last };
299 /^include$/ and last;
302 warn "unhandled preprocessor directive: $dir\n";
306 my @e = split /\s*\|\s*/, $line;
308 my($flags, $ret, $name, @args) = @e;
309 if ($name =~ /^[^\W\d]\w*$/) {
313 ($ret) = trim_arg($ret);
316 flags => { map { $_, 1 } $flags =~ /./g },
319 cond => ppcond(\@pps),
322 elsif ($name =~ /^[^\W\d]\w*-E<gt>[^\W\d]\w*$/) {
323 # silenty ignore entries of the form
324 # PL_parser-E<gt>linestr
325 # which documents a struct entry rather than a function
328 warn "mysterious name [$name] in $file, line $.\n";
343 my @args = map { "@$_" } @{$f->{args}};
345 my $pTHX_ = exists $f->{flags}{n} ? "" : "pTHX_ ";
346 $proto = "$f->{ret} $f->{name}" . "($pTHX_" . join(', ', @args) . ')';
355 my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
360 if ($r < 5 || ($r == 5 && $v < 6)) {
362 die "invalid version '$ver'\n";
366 $ver = sprintf "%d.%03d", $r, $v;
367 $s > 0 and $ver .= sprintf "_%02d", $s;
372 return sprintf "%d.%d.%d", $r, $v, $s;
379 if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) {
382 elsif ($ver !~ /^\d+\.[\d_]+$/) {
383 die "cannot parse version '$ver'\n";
389 my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
394 if ($r < 5 || ($r == 5 && $v < 6)) {
396 die "cannot parse version '$ver'\n";