The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use XML::Parser;
use SIL::Shoe::Data;
use IO::File;
use Getopt::Std;
use Encode;
use Encode::TECkit;

getopts('c:');

our (%teckits);

unless (defined $ARGV[1] && defined $opt_c)
{
    die <<'EOT';
    shed -c config.xml infile outfile
Copies Shoebox database from infile to outfile applying the instructions in
config.xml
EOT
}

$sh = SIL::Shoe::Data->new($ARGV[0]) || die "Can't open $ARGV[0] for reading";
$ofh = IO::File->new("> $ARGV[1]") || die "Can't create $ARGV[1]";

$ofh->print("\\_sh $sh->{' Version'} $sh->{' CSum'} $sh->{' Type'}\n");
$ofh->print("\\_DateStampHasFourDigitYear\n") if ($sh->{' DateStamp'} == 4);

$xfh = IO::File->new("< $opt_c") || die "Can't open $opt_c";
$xtext = join('', <$xfh>);
$xml = XML::Parser->new(Style => 'Subs', Pkg => 'SIL::Shoe::Shed',
        Handlers => {'Char' => sub {
            use bytes;
            my ($xml, $text) = @_;
            $xml->{' stack'}[-1]{' text'} .= $text if (scalar @{$xml->{' stack'}});
            }},
        ' stack' => [], ' sh' => $sh, ' fields' => [],
        'Non-Expat-Options' => {' stack' => 1, ' sh' => 1, ' fields' => 1});
    
while ($sh->readrecord($xml->{' fields'}))
{
    $ofh->print("\n");
    $xml->parse($xtext);
    foreach $f (@{$xml->{' fields'}})
    {
        my ($mk) = $f;
        $mk =~ s/\s+.*$//o;
        $ofh->print("\\$mk $sh->{$f}\n");
    }
}

package SIL::Shoe::Shed;

sub gettext
{
    my ($xml, $attrs) = @_;
    
    if (defined $attrs->{'sfm'})
    { return $xml->{' sh'}{$attrs->{'sfm'}}; }
    else
    {
        my ($str) = $attrs->{' text'};
        $str =~ s/^\s*//o;
        $str =~ s/\s*$//o;
        return $str;
    }
}

sub insert
{
    my ($xml, $tag, %attrs) = @_;
    
    push (@{$xml->{' stack'}}, {%attrs});
}

sub insert_
{
    my ($xml, $tag) = @_;
    my ($attrs) = pop(@{$xml->{' stack'}});
    my ($mk) = $attrs->{'sfm'};
#    my ($str) = encode_utf8($attrs->{' text'});
    my ($str) = $attrs->{' text'};
    my ($i);
    
    for ($i = 0; $i < scalar @{$xml->{' fields'}}; $i++)
    { last if ($xml->{' fields'}[$i] eq $attrs->{'loc'}); }
    return if ($i >= scalar @{$xml->{' fields'}});
    
    while (defined $xml->{' sh'}{$mk})
    {
        if ($mk =~ m/\d$/o)
        { $mk++; }
        else
        { $mk = "$mk 0"; }
    }
    $str =~ s/^\s*//o;
    $str =~ s/\s*$//o;
    $xml->{' sh'}{$mk} = $str;
    splice(@{$xml->{' fields'}}, $i + ($attrs->{'relative'} eq 'after' ? 1 : 0), 0, $mk);
}

sub eval
{
    my ($xml, $tag, %attrs) = @_;
    
    push (@{$xml->{' stack'}}, {%attrs});
}

sub eval_
{
    my ($xml, $tag) = @_;
    my ($attrs) = pop(@{$xml->{' stack'}});
    
    $_ = gettext($xml, $attrs);
    chomp;
    my ($res) = eval "$attrs->{' text'}";
    $xml->{' stack'}[-1]{' text'} = $res;
}

sub teckit
{
    my ($xml, $tag, %attrs) = @_;
    
    push (@{$xml->{' stack'}}, {%attrs});
    unless (defined $teckits{$attrs{'file'}})
    {
        $teckits{$attrs{'file'}} = Encode::TECkit->new($attrs{'file'}) || die "Can't find teckit file $attrs{'file'}";
    }
}

sub teckit_
{
    my ($xml, $tag) = @_;
    my ($attrs) = pop(@{$xml->{' stack'}});
    my ($str) = gettext($xml, $attrs);
    
    if ($attrs->{'dir'} eq 'encode')
    {
        $xml->{' stack'}[-1]{' text'} = $teckits{$attrs->{'file'}}->encode(Encode::decode_utf8($str));
    }
    else
    {
	my ($res) = $teckits{$attrs->{'file'}}->decode($str);
        $xml->{' stack'}[-1]{' text'} = Encode::encode_utf8($res);
    }
}