1#!/usr/bin/perl
2# 4.9.2009, Sampo Kellomaki (sampo@iki.fi)
3# $Id: xml-pretty.pl,v 1.1 2009-09-05 02:23:41 sampo Exp $
4#
5# Pretty Print XML  --  Make XML almost human readable
6#
7# Usage: cat foo.saml | ./zxdecode | ./xml-pretty.pl
8# tailf /var/zxid/log/xml.dbg | ./xml-pretty.pl
9use Data::Dumper;
10
11$ascii = 2;
12
13# See https://wiki.archlinux.org/index.php/Color_Bash_Prompt
14sub red   { $ascii > 1 ? "\e[1;31m$_[0]\e[0m" : $_[0]; }  # red text
15#sub green { $ascii > 1 ? "\e[1;32m$_[0]\e[0m" : $_[0]; }
16#sub red    { $ascii > 1 ? "\e[1;41m$_[0]\e[0m" : $_[0]; }  # red background, black bold text
17sub green  { $ascii > 1 ? "\e[1;42m$_[0]\e[0m" : $_[0]; }
18sub redy   { $ascii > 1 ? "\e[41m$_[0]\e[0m" : $_[0]; }    # red background, black text (no bold)
19sub greeny { $ascii > 1 ? "\e[42m$_[0]\e[0m" : $_[0]; }
20sub yely { $ascii > 1 ? "\e[43m$_[0]\e[0m" : $_[0]; }
21sub bluy { $ascii > 1 ? "\e[46m$_[0]\e[0m" : $_[0]; }
22
23$indent = '';
24
25sub xml_pretty {
26    my $res = '';
27    my ($x,$at,$noindent);
28    #warn "start res($res) indent($indent) data($_[0])";  # tail dup seems perl error
29    for $x (split /(<\/?\w.*?>)/, $_[0]) {
30	next if !length $x;
31	#print "*";
32	if ($x !~ /^</) {
33	    $last_tag = undef;
34	    if (length $x < 40) {
35		if ($x =~ /^\s*$/s) {
36		    #$res .= "\n";
37		    #warn "HERE1($x)";
38		} else {
39		    #warn "HERE3($x)";
40		    chomp $res;
41		    $res .= green($x);
42		}
43		$noindent = 1;
44	    } else {
45		my $xx = $x;
46		chomp $xx;
47		if ($xx =~ /^\s*$/s) {
48		    $res .= "\n";
49		    #warn "HERE($xx)";
50		} else {
51		    #warn "HERE2($xx)";
52		    $res .= $indent.greeny($xx)."\n";
53		}
54	    }
55	    next;
56	}
57	if ($x =~ /^<!--/) {
58	    $last_tag = undef;
59	    $x =~ s/\e\[\d+m//g;
60	    $res .= bluy($x)."\n";
61	    $indent = '';
62	    next;
63	}
64	if ($x =~ /^<\?/) {
65	    $last_tag = undef;
66	    $res .= "$indent$x\n";
67	    next;
68	}
69	if ($x =~ /^<\//) {           # close tag
70	    substr($indent,-2) = '';
71	    $rx = red($x);
72	    if ($noindent) {
73		$res .= "</>";
74	    } else {
75		$xx = substr($x,2,-1);
76		#warn "       x($xx)      indent($indent)\nlast_tag($last_tag) last_indent($last_indent)";
77		if ($xx eq $last_tag && $indent eq $last_indent) {
78		    die "Res does not end in > res($res)" if substr($res,-2) ne ">\n";
79		    chop $res;
80		    chop $res;
81		    $res .= "/>\n";
82		} else {
83		    $res .= "$indent$rx\n";
84		}
85	    }
86	    $last_tag = undef;
87	    next;
88	}
89	if ($noindent) {
90	    $noindent = 0;
91	    $res .= "\n";
92	}
93	#            1               12   3   32 4  4
94	if ($x =~ /^<([A-Za-z0-9_:-]+)(\s+(.*?))?(\/)?>$/) {
95	    $last_tag = $1;
96	    $last_indent = $indent;
97	    $res .= "$indent<".red($1);
98	    my @ats = split / /, $3;
99	    if ($#ats == 0) {
100		my ($name,$val) = split /=/, $ats[0], 2;
101		$res .= " $name=".yely($val);
102	    } else {
103		for $at (@ats) {
104		    my ($name,$val) = split /=/, $at, 2;
105		    $res .= "\n$indent    $name=".yely($val);
106		}
107	    }
108	    if ($4) {
109		$res .= "/>\n";
110		$last_tag = undef;
111	    } else {
112		$res .= ">\n";
113		$indent .= '  ';
114	    }
115	} else {
116	    die "unprocessable start tag($x) (tag must be completely on one line)";
117	}
118    }
119    return $res;
120}
121
122while (defined($line = <STDIN>)) {
123    #$line =~ tr[\r][];
124    print xml_pretty($line);
125}
126
127__END__
128