Category: Utilities
Author/Contact Info mirod
Description: xml_pp (or whatever you want to call it) uses the amazing power of XML::Twig to pretty print XML files
#!/usr/bin/perl -w
use strict;

use XML::Twig;
use File::Temp qw/ tempfile/; 

my @styles= qw( none nsgmls nice indented record record_c); # from XML
+::Twig
my $styles= join '|', @styles;                              # for usag
+e
my %styles= map { $_ => 1} @styles;                         # to check
+ option

my $DEFAULT_STYLE= 'indented';

my $USAGE= "usage: $0 [-v] [-i<extension>] [-s ($styles)] <files>";

my %opt;
while( $ARGV[0]=~ m{^-})
  { my $opt= shift @ARGV;
    if(    $opt eq '-v')  { $opt{verbose}= 1; }
    elsif( $opt eq '-s')  { $opt{style}= shift @ARGV;
                            die $USAGE unless( $styles{$opt{style}});
                          }
    elsif( $opt=~ m{^-i(.*)$})
                          { $opt{in_place}= 1;
                    $opt{backup}= $1 ||'';
                          }
    elsif( $opt eq '--')  { last;       }
    else                  { die $USAGE; }
  }

$opt{style} ||= $DEFAULT_STYLE;
  

my $t= XML::Twig->new( twig_handlers => { _all_ => sub { $_[0]->flush 
+} },
                       pretty_print  => $opt{style},
               error_context => 1,
                     );
             
foreach my $file (@ARGV)
  { print STDERR "$file\n" if( $opt{verbose});
    my $tempfile;
    if( $opt{in_place})
      { my $backup;
        if( $opt{backup})  
          { $backup= "$file$opt{backup}";
            rename( $file, $backup) or die "cannot create backup file 
+$backup: $!";
            open( OUTPUT, ">$file") or die "cannot update file $file: 
+$!";
        select OUTPUT;
        $file= $backup;
      }
    else
      { (undef, $tempfile)= tempfile( DIR => '.');
            rename( $file, $tempfile) or die "cannot create tempfile f
+ile $tempfile: $!";
            open( OUTPUT, ">$file") or die "cannot update file $file: 
+$!";
        select OUTPUT;
        $file= $tempfile;
          }
      }
    $t->parsefile( $file);
    select STDOUT;
    if( defined $tempfile)
      { unlink $tempfile or die "cannot unlink temp file $tempfile: $!
+"; }
  }
  
__END__

=head1 NAME

xml_pp

=head1 SYNOPSYS

xml_pp [-v] [-i[<extension>]] [-s (none|nsgmls|nice|indented|record|re
+cord_c)] <files>

=head1 DESCRIPTION

XML pretty printer using XML::Twig styles

=head1 OPTIONS

=over 4

=item -i[<extension>] : edits the file(s) in place, if an extension is
+ provided
(no space between -i and the extension) then the original file is back
+ed-up
with that extension

=item -v : verbose (list the current file being processed)

=item -s <style> : the style to use for pretty printing (see XML::Twig
+ docs for
the exact description of those styles), 'indented' by default

=back

=head1 BUGS

Elements with mixed content that start with an embedded element get an
+ extra \n 

  <elt><b>b</b>toto<b>bold</b></elt>

will be output as 

  <elt>
    <b>b</b>toto<b>bold</b></elt>


=head1 TODO

Add ways to pass options to XML::Twig, either through the command line
+ or 
through a config file
Replies are listed 'Best First'.
Re: xml_pp : YAXPP (Yet Another XML Pretty Printer)
by mirod (Canon) on Jun 13, 2002 at 16:50 UTC

    This was a first version, I have improved it a lot since then (and fixed a bunch of bugs in XML::Twig too!). The latest, bestest version will be maintained on xmltwig.com, in the tools section.