package XML;

use strict;

use Exporter;

use vars qw(@ISA $xml_indent_string);

=head2 tag

create an XML tag

=cut

sub tag {
	my ($package, $element) = splice @_, 0, 2;
	my $single = ($element =~ s|/$||) ? "/" : "";

	my $attr = "";
	while (my ($name, $value) = splice @_, 0, 2) {
		$attr .= qq{ $name="}.$package->encode($value).'"' if defined $value;
	}

	return "<$element$attr$single>\n";
}

=head2 convertchar

Encode UTF-8 strings to escape XML entities

=cut

sub convertchar {
	# Do clever stuff to reencode UTF-8 characters to entities
	my $package = shift;
	my $arg = shift;
	my $arg2 = shift;
	my $res = ord($arg);
	my $mask = 0x7f;
	if ($arg2) {
		for my $i (unpack('C*',$arg2)) {
			$res = ($res << 6) | ($i & 0x3f);
			$mask = ($mask << 5) | 0x1f;
		}
	}

	return "&#" . ($res & $mask) . ";";
}

=head2 encode

Encode a string to escape XML/HTML entities (eg: "&" = "&amp;")

=cut

sub encode {
	#convert special and multi-byte utf-8 bytes to entities
	my ($package, $str) = @_;

	return "" if $str eq "";

	if ($str =~ /^\s*$/) {
		return $str;
	}

	$str =~ s/&/&amp;/g;
	$str =~ s/</&lt;/g;
	$str =~ s/>/&gt;/g;
	$str =~ s/\n/&#010;/g;
	$str =~ s/\"/&quot;/g;
	$str =~ s/([\xc0-\xff])([\x80-\xbf]*)/$package->convertchar($1,$2)/eg;

	return $str;
}

=head2 sjoin

like join, but empty strings do not attract multiple separators!

=cut

sub sjoin {
	my $package = shift;
	my $join = shift;
	return "" unless @_;
	my $ret = shift;
	for (@_) { $ret .= "$join$_" unless $_ eq ""; }
	return $ret;
}

=head2 indent

indent a block of text

xml_index($block, $step);

$step may be negative, to strip indentation

=cut

sub indent {
	my ($package, $block, $step) = @_;
	$step = 1 unless defined $step;
	return $package->_indent($block, $step, $xml_indent_string);
}

=head2 $XML::xml_indent_string

string to use for indentation ( "\t" by default )

=cut

$xml_indent_string = "\t";

=head2 _indent

indent a block of text

=cut

sub _indent {
	my ($package, $block, $step, $sep) = @_;
	$step = 1 unless defined $step;
	$sep = "\t" unless defined $sep;
	if ($step > 0) {
		my $indent = $sep x $step;
		$block =~ s/(^|\n(?!$))/$1$indent/sg;
	} elsif ($step < 0) {
		my $indent = $sep x -$step;
		$block =~ s/(^|\n(?!$))$indent/$1/sg;
	}
	return $block;
}

1
