#!/usr/bin/perl
sub
usage {
die
<<EOF }
@_
usage: $0 -c <C.C.C>
-s <C.C.C> <N.N.N>
-u
-i <C.C.C> <N.N.N>
-c check files and warn if any known string values (eg
PERL_SUBVERSION) don't match the specified version
-s scan files and produce list of possible change lines to stdout
-u read in the scan file from stdin, and change all the lines specified
-i scan files and make changes inplace
C.C.C the current perl version, eg 5.10.0
N.N.N the new perl version, eg 5.10.1
EOF
my
%opts
;
getopts(
'csui'
, \
%opts
) or usage;
if
(
$opts
{u}) {
@ARGV
== 0 or usage(
'no version numbers should be specified'
);
@ARGV
=
qw(99.99.99 99.99.99)
;
}
elsif
(
$opts
{c}) {
@ARGV
== 1 or usage(
'required one version number'
);
push
@ARGV
,
$ARGV
[0];
}
else
{
@ARGV
== 2 or usage(
'require two version numbers'
);
}
usage(
'only one of -c, -s, -u and -i'
)
if
keys
%opts
> 1;
my
(
$oldx
,
$oldy
,
$oldz
) =
$ARGV
[0] =~ /^(\d+)\.(\d+)\.(\d+)$/
or usage(
"bad version: $ARGV[0]"
);
my
(
$newx
,
$newy
,
$newz
) =
$ARGV
[1] =~ /^(\d+)\.(\d+)\.(\d+)$/
or usage(
"bad version: $ARGV[1]"
);
my
$old_decimal
=
sprintf
"%d.%03d%03d"
,
$oldx
,
$oldy
,
$oldz
;
my
@maps
= (
[
qr{^((?:api_)?version(?:=|\s+)'?) (\d+) ('?) (?!\.)}
x,
sub
{ $2,
"$1$newy$3"
},
$oldy
,
qr/config/
,
],
[
qr{^(subversion(?:=|\s+)'?) (\d+) ('?) (?!\.)}
x,
sub
{ $2,
"$1$newz$3"
},
$oldz
,
qr/config/
,
],
[
qr{^(api_subversion(?:=|\s+)'?) (\d+) ('?) (?!\.)}
x,
sub
{ $2, (
$newy
% 2) ?
"$1$newz$3"
:
"${1}0$3"
},
(
$oldy
% 2) ?
$oldz
: 0,
qr/config/
,
],
[
qr{^(api_versionstring(?:=|\s+)'?) ([\d\.]+) ('?) (?!\.)}
x,
sub
{ $2, (
$newy
% 2) ?
"$1$newx.$newy.$newz$3"
:
"$1$newx.$newy.0$3"
},
(
$oldy
% 2) ?
"$oldx.$oldy.$oldz"
:
"$oldx.$oldy.0"
,
qr/config/
,
],
[
qr{(version\s+'?) (\d+) ('?\s+subversion\s+'?) (\d+) ('?) (?!\.)}
x,
sub
{
"$2-$4"
,
"$1$newy$3$newz$5"
},
"$oldy-$oldz"
,
qr/config/
,
],
[
qr{\b (PERL_(?:API_)?VERSION(?:=|\s+)'?) (\d+) ('?) (?!\.)}
x,
sub
{ $2,
"$1$newy$3"
},
$oldy
,
],
[
qr{\b (PERL_SUBVERSION(?:=|\s+)'?) (\d+) ('?) (?!\.)}
x,
sub
{ $2,
"$1$newz$3"
},
(
$oldy
% 2) ?
$oldz
: 0,
],
[
qr{\b (PERL_API_SUBVERSION(?:=|\s+)'?) (\d+) ('?) (?!\.)}
x,
sub
{ $2, (
$newy
% 2) ?
"$1$newz$3"
:
"${1}0$3"
},
$oldz
,
],
[
qr{\b perl-(\d+\^\.\d+\^\.\d+) \b}
x,
sub
{ $1,
"perl-$newx^.$newy^.$newz"
},
undef
,
],
[
qr{\b ($oldx _ $oldy _$oldz) \b}
x,
sub
{ $1, (
$newx
.
'_'
.
$newy
.
'_'
.
$newz
)},
undef
,
],
[
qr{ $oldx\.$oldy\.$oldz \b}
x,
sub
{
""
,
"$newx.$newy.$newz"
},
undef
,
],
[
qr{ $old_decimal \b}
x,
sub
{
""
,
sprintf
"%d.%03d%03d"
,
$newx
,
$newy
,
$newz
},
undef
,
],
[
qr{\b ((?:lib)?) perl (\d\d\d) (s?) \b }
x,
sub
{$2,
"$1perl$newx$newy$3"
},
"$oldx$oldy"
,
qr/win32|hints/
,
],
[
qr{(/)(\d\.\d{2}
)(["'/])},
sub
{ $2,
"$1$newx.$newy$3"
},
"$oldx.$oldy"
,
qr/uconfig/
,
],
);
my
%SKIP_FILES
=
map
{ (
$_
=> 1) }
qw(
Changes
intrpvar.h
MANIFEST
Porting/Maintainers.pl
Porting/acknowledgements.pl
Porting/corelist-perldelta.pl
Porting/epigraphs.pod
Porting/how_to_write_a_perldelta.pod
Porting/release_managers_guide.pod
Porting/release_schedule.pod
Porting/bump-perl-version
pp_ctl.c
)
;
my
@SKIP_DIRS
=
qw(
dist
ext
lib
pod
cpan
t
)
;
my
@mani_files
=
sort
keys
%{ExtUtils::Manifest::maniread(
'MANIFEST'
)};
my
%mani_files
=
map
{ (
$_
=> 1) }
@mani_files
;
die
"No entries found in MANIFEST; aborting\n"
unless
@mani_files
;
if
(
$opts
{c} or
$opts
{s} or
$opts
{i}) {
do_scan();
}
elsif
(
$opts
{u}) {
do_update();
}
else
{
usage(
'one of -c, -s or -u must be specified'
);
}
exit
0;
sub
do_scan {
for
my
$file
(
@mani_files
) {
next
if
grep
$file
=~ m{^
$_
/},
@SKIP_DIRS
;
if
(
$SKIP_FILES
{
$file
}) {
warn
"(skipping $file)\n"
;
next
;
}
open
my
$fh
,
'<'
,
$file
;
my
$header
= 0;
my
@stat
=
stat
$file
;
my
$mode
=
$stat
[2];
my
$file_changed
= 0;
my
$new_contents
=
''
;
while
(
my
$line
= <
$fh
>) {
my
$oldline
=
$line
;
my
$line_changed
= 0;
for
my
$map
(
@maps
) {
my
(
$pat
,
$sub
,
$expected
,
$file_pat
) =
@$map
;
next
if
defined
$file_pat
and
$file
!~
$file_pat
;
next
unless
$line
=~
$pat
;
my
(
$got
,
$replacement
) =
$sub
->();
if
(
$opts
{c}) {
next
unless
defined
$expected
and
$got
ne
$expected
;
}
$line
=~ s/
$pat
/
$replacement
/
or
die
"Internal error: substitution failed: [$pat]\n"
;
if
(
$line
ne
$oldline
) {
$line_changed
= 1;
last
;
}
}
$new_contents
.=
$line
if
$opts
{i};
if
(
$line_changed
) {
$file_changed
= 1;
if
(
$opts
{s}) {
print
"\n$file\n"
unless
$header
;
$header
=1;
printf
"\n%5d: -%s +%s"
, $.,
$oldline
,
$line
;
}
}
}
if
(
$opts
{i} &&
$file_changed
) {
warn
"Updating $file inplace\n"
;
open
my
$fh
,
'>'
,
$file
;
binmode
$fh
;
print
$fh
$new_contents
;
close
$fh
;
chmod
$mode
& 0777,
$file
;
}
}
warn
"(skipped $_/*)\n"
for
@SKIP_DIRS
;
}
sub
do_update {
my
%changes
;
my
$file
;
my
$line
;
while
(<STDIN>) {
next
unless
/\S/;
if
(/^(\S+)$/) {
$file
= $1;
die
"No such file in MANIFEST: '$file'\n"
unless
$mani_files
{
$file
};
die
"file already seen; '$file'\n"
if
exists
$changes
{
$file
};
undef
$line
;
}
elsif
(/^\s+(\d+): -(.*)/) {
my
$old
;
(
$line
,
$old
) = ($1,$2);
die
"$.: old line without preceding filename\n"
unless
defined
$file
;
die
"Dup line number: $line\n"
if
exists
$changes
{
$file
}{
$line
};
$changes
{
$file
}{
$line
}[0] =
$old
;
}
elsif
(/^\s+\+(.*)/) {
my
$new
= $1;
die
"$.: replacement line seen without old line\n"
unless
$line
;
$changes
{
$file
}{
$line
}[1] =
$new
;
undef
$line
;
}
else
{
die
"Unexpected line at ;line $.: $_\n"
;
}
}
my
%contents
;
for
my
$file
(
sort
keys
%changes
) {
open
my
$fh
,
'<'
,
$file
;
binmode
$fh
;
$contents
{
$file
} = [ <
$fh
> ];
chomp
@{
$contents
{
$file
}};
close
$fh
;
my
$entries
=
$changes
{
$file
};
for
my
$line
(
keys
%$entries
) {
die
"$file: no such line: $line\n"
unless
defined
$contents
{
$file
}[
$line
-1];
if
(
$contents
{
$file
}[
$line
-1] ne
$entries
->{
$line
}[0]) {
die
"$file: line mismatch at line $line:\n"
.
"File: [$contents{$file}[$line-1]]\n"
.
"Config: [$entries->{$line}[0]]\n"
}
$contents
{
$file
}[
$line
-1] =
$entries
->{
$line
}[1];
}
}
for
my
$file
(
sort
keys
%contents
) {
my
$nfile
=
"$file-new"
;
die
"$nfile already exists in MANIFEST; aborting\n"
if
$mani_files
{
$nfile
};
}
for
my
$file
(
sort
keys
%contents
) {
my
$nfile
=
"$file-new"
;
open
my
$fh
,
'>'
,
$nfile
;
binmode
$fh
;
print
$fh
$_
,
"\n"
for
@{
$contents
{
$file
}};
close
$fh
;
my
@stat
=
stat
$file
;
my
$mode
=
$stat
[2];
die
"stat $file fgailed to give a mode!\n"
unless
defined
$mode
;
chmod
$mode
& 0777,
$nfile
;
}
for
my
$file
(
sort
keys
%contents
) {
my
$nfile
=
"$file-new"
;
warn
"updating $file ...\n"
;
rename
$nfile
,
$file
;
}
}