#!/usr/bin/perl -w
use Data::Dumper;
use File::Find;
use Getopt::Std;
use IO::File;
use strict;
my ( $EXCLUDE, $HELP, $QUIET, $TEST );
my $usage = <<EOF;
Usage: $0 -hqt [-e <regexp>] <directory> [<directory>...]
-e <regexp>: Exclude paths matching <regexp> case-insensitive. e.g. "(.gif|.jpg)$"
-h: Display help message and exit
-q: Quiet mode, do not report normal processing of files
-t: Do not actually change files, just report what changes would be made
EOF
my $helpmsg = <<EOF;
This utility converts existing components to use new syntax
introduced in Mason 0.8.
1. Old-style mc_commands (mc_comp, mc_file, etc.) are converted to
new-style \$m methods (\$m->comp, \$m->file, etc.) See Commands.pod for
all the conversions to be performed.
2. References to request variable \$REQ are converted to \$m.
All directories will be traversed recursively. We STRONGLY recommend
that you backup your components, and/or use the -t flag to preview,
before running this program for real. Files are modified
destructively and no automatic backups are created.
EOF
my $warning = <<EOF;
Warning: All directories will be traversed recursively. Files are
modified destructively and no automatic backups are created.
EOF
sub usage {
print $usage;
exit;
}
sub main {
my (%opts);
getopts( 'e:hlqtu', \%opts );
( $EXCLUDE, $HELP, $QUIET, $TEST ) = @opts{qw(e h q t)};
if ($HELP) { print "$helpmsg\n$usage"; exit }
if ( !@ARGV ) { print "$usage\n$helpmsg"; exit }
my @dirs = @ARGV;
if ( !$TEST ) {
print "*** Mason 0.8 Conversion ***\n\n";
print "Quiet mode.\n" if defined($QUIET);
print "Excluding paths matching ($EXCLUDE).\n" if defined($EXCLUDE);
print "Processing "
. ( @dirs == 1 ? "directory " : "directories " )
. join( ",", @dirs ) . "\n";
print $warning;
print "\nProceed? [n] ";
exit if ( ( my $ans = <STDIN> ) !~ /[Yy]/ );
}
my $sub = sub {
if ( -f $_ && -s _ ) {
return
if defined($EXCLUDE) && "$File::Find::dir/$_" =~ /$EXCLUDE/i;
convert( $_, "$File::Find::dir/$_" );
}
};
find( $sub, @dirs );
}
sub convert {
my ( $file, $path ) = @_;
my $buf;
my $infh = new IO::File $file;
if ( !$infh ) { warn "cannot read $path: $!"; return }
{ local $/ = undef; $buf = <$infh> }
my $c = 0;
my ( @changes, @failures );
my $report = sub {
push( @changes, $_[1] ? "$_[0] --> $_[1]" : "removed $_[0]" );
};
my $report_failure = sub { push( @failures, $_[0] ) };
#
# Convert mc_ commands to $m-> method equivalents
#
# Easy substitutions
#
my $easy_cmds = join(
"|",
qw(abort cache cache_self call_self comp comp_exists dhandler_arg file file_root out time)
);
if ( !$TEST ) {
$c += ( $buf =~ s{mc_($easy_cmds)(?![A-Za-z0-9 _])}{"\$m->$1"}geo );
}
else {
while ( $buf =~ m{(mc_($easy_cmds)(?![A-Za-z0-9 _]))}go ) {
$report->( $1, "\$m->$2" );
}
}
# Boilerplate substitutions for methods with no arguments
my @subs = (
[ 'mc_auto_comp', '$m->fetch_next->path' ],
[ 'mc_caller', '$m->callers(1)->path' ],
[ 'mc_comp_source', '$m->current_comp->source_file' ],
[ 'mc_comp_stack', 'map($_->title,$m->callers)' ],
);
foreach my $sub (@subs) {
my ( $mc_cmd, $repl ) = @$sub;
if ( !$TEST ) {
$c += ( $buf =~ s{$mc_cmd(\s*\(\))?(?!\s*[\(])}{$repl}ge );
}
else {
while ( $buf =~ m{($mc_cmd(\s*\(\))?(?!\s*[\(]))}g ) {
$report->( $1, $repl );
}
}
}
# Boilerplate substitutions for methods with arguments
@subs = (
[ 'mc_auto_next', '$m->call_next' ],
);
foreach my $sub (@subs) {
my ( $mc_cmd, $repl ) = @$sub;
if ( !$TEST ) {
$c += ( $buf =~ s{$mc_cmd}{$repl}ge );
}
else {
while ( $buf =~ m{($mc_cmd)}g ) {
$report->( $1, $repl );
}
}
}
# mc_comp_source with simple argument
if ( !$TEST ) {
$c
+= ( $buf
=~ s{mc_comp_source\s*\(([^\(\)]+)\)}{"\$m->fetch_comp($1)->source_file"}ge
);
}
else {
while ( $buf =~ m{(mc_comp_source\s*\(([^\(\)]+)\))}g ) {
$report->( $1, "\$m->fetch_comp($2)->source_file" );
}
}
# mc_suppress_http_header with and without arguments
if ( !$TEST ) {
$c += ( $buf =~ s{mc_suppress_http_header\s*(?!\s*\();?}{}g );
$c += ( $buf =~ s{mc_suppress_http_header\s*\([^\(\)]*\)\s*;?}{}g );
}
else {
while ( $buf =~ m{(mc_suppress_http_header\s*(?!\s*\();?)}g ) {
$report->( $1, "" );
}
while ( $buf =~ m{(mc_suppress_http_header\s*\([^\(\)]*\)\s*;?)}g ) {
$report->( $1, "" );
}
}
#
# Convert $REQ to $m
#
if ( !$TEST ) {
$c += ( $buf =~ s{\$REQ(?![A-Za-z0-9_])}{\$m}go );
}
else {
while ( $buf =~ m{(\$REQ(?![A-Za-z0-9_]))}go ) {
$report->( $1, "\$m" );
}
}
# Report substitutions we can't handle
foreach my $cmd (qw(mc_comp_source mc_suppress_http_header)) {
if ( $buf =~ m{$cmd\s*\([^\)]*\(} ) {
$report_failure->("Can't convert $cmd with complex arguments");
}
}
if ( $buf =~ m{mc_date} ) {
$report_failure->("Can't convert mc_date");
}
if ($TEST) {
if (@changes) {
print scalar(@changes) . " substitutions in $path:\n";
print join( "\n", @changes ) . "\n";
}
}
if ( $c && !$TEST ) {
print "$c substitutions in $path\n" if !$QUIET;
my $outfh = new IO::File ">$file";
if ( !$outfh ) { warn "cannot write $path: $!"; return }
$outfh->print($buf);
}
foreach my $failure (@failures) {
print "** Warning: $failure; must fix manually\n";
}
print "\n" if ( ( $TEST && @changes ) || @failures );
}
main();