#!/usr/bin/perl -w
eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
if 0; # not running under some shell
# $Id: /xmltwig/trunk/tools/xml_pp/xml_pp 32 2008-01-18T13:11:52.128782Z mrodrigu $
use strict;
use XML::Twig;
use File::Temp qw/tempfile/;
use File::Basename qw/dirname/;
my @styles= XML::Twig->_pretty_print_styles; # from XML::Twig
my $styles= join '|', @styles; # for usage
my %styles= map { $_ => 1} @styles; # to check option
my $DEFAULT_STYLE= 'indented';
my $USAGE= "usage: $0 [-v] [-i] [-s ($styles)] [-p ] [-e ] [-l] [-f ] []";
# because of the -i.bak option I don't think I can use one of the core
# option processing modules, so it's custom handling and no clusterization :--(
my %opt= process_options(); # changes @ARGV
my @twig_options=( pretty_print => $opt{style},
error_context => 1,
);
if( $opt{preserve_space_in})
{ push @twig_options, keep_spaces_in => $opt{preserve_space_in};}
if( $opt{encoding})
{ push @twig_options, output_encoding => $opt{encoding};
}
else
{ push @twig_options, keep_encoding => 1; }
# in normal (ie not -l) mode tags are output as soon as possible
push @twig_options, twig_handlers => { _all_ => sub { $_[0]->flush } }
unless( $opt{load});
my $t= XML::Twig->new( @twig_options);
if( @ARGV)
{ foreach my $file (@ARGV)
{ print STDERR "$file\n" if( $opt{verbose});
my $tempfile;
if( $opt{in_place})
{ (undef, $tempfile)= tempfile( DIR => dirname( $file));
open( OUTPUT, ">$tempfile") or die "cannot create tempfile $tempfile: $!";
select OUTPUT;
}
$t= $t->safe_parsefile( $file);
if( $t)
{ if( $opt{load}) { $t->print; }
select STDOUT;
if( $opt{in_place})
{ if( $opt{backup})
{ my $backup= backup( $file, $opt{backup});
rename( $file, $backup) or die "cannot create backup file $backup: $!";
}
rename( $tempfile, $file) or die "cannot overwrite file $file: $!";
}
}
else
{ if( defined $tempfile)
{ unlink $tempfile or die "cannot unlink temp file $tempfile: $!"; }
die $@;
}
}
}
else
{ $t->parse( \*STDIN);
if( $opt{load}) { $t->print; }
}
sub process_options
{ my %opt;
while( @ARGV && ($ARGV[0]=~ m{^-}) )
{ my $opt= shift @ARGV;
if( ($opt eq '-v') || ($opt eq '--verbose') )
{ die $USAGE if( $opt{verbose});
$opt{verbose}= 1;
}
elsif( ($opt eq '-s') || ($opt eq '--style') )
{ die $USAGE if( $opt{style});
$opt{style}= shift @ARGV;
die $USAGE unless( $styles{$opt{style}});
}
elsif( ($opt=~ m{^-i(.*)$}) || ($opt=~ m{^--in_place(.*)$}) )
{ die $USAGE if( $opt{in_place});
$opt{in_place}= 1;
$opt{backup}= $1 ||'';
}
elsif( ($opt eq '-p') || ($opt eq '--preserve') )
{ my $tags= shift @ARGV;
my @tags= split /\s+/, $tags;
$opt{preserve_space_in} ||= [];
push @{$opt{preserve_space_in}}, @tags;
}
elsif( ($opt eq '-e') || ($opt eq '--encoding') )
{ die $USAGE if( $opt{encoding});
$opt{encoding}= shift @ARGV;
}
elsif( ($opt eq '-l') || ($opt eq '--load'))
{ die $USAGE if( $opt{load});
$opt{load}=1;
}
elsif( ($opt eq '-f') || ($opt eq '--files') )
{ my $file= shift @ARGV;
push @ARGV, files_from( $file);
}
elsif( ($opt eq '-h') || ($opt eq '--help'))
{ system "pod2text", $0; exit; }
elsif( $opt eq '--')
{ last; }
else
{ die $USAGE; }
}
$opt{style} ||= $DEFAULT_STYLE;
return %opt;
}
# get the list of files (one per line) from a file
sub files_from
{ my $file= shift;
open( FILES, "<$file") or die "cannot open file $file: $!";
my @files;
while( ) { chomp; push @files, $_; }
close FILES;
return @files;
}
sub backup
{ my( $file, $extension)= @_;
my $backup;
if( $extension=~ m{\*})
{ ($backup= $extension)=~ s{\*}{$file}g; }
else
{ $backup= $file.$extension; }
return $backup;
}
__END__
=head1 NAME
xml_pp - xml pretty-printer
=head1 SYNOPSYS
xml_pp [options] []
=head1 DESCRIPTION
XML pretty printer using XML::Twig
=head1 OPTIONS
=over 4
=item -i[]
edits the file(s) in place, if an extension is provided (no space between
C<-i> and the extension) then the original file is backed-up with that extension
The rules for the extension are the same as Perl's (see perldoc perlrun): if
the extension includes no "*" then it is appended to the original file name,
If the extension does contain one or more "*" characters, then each "*" is
replaced with the current filename.
=item -s