//
// tardy - a tar post-processor
// Copyright (C) 2011 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

#include <libtardy/ac/string.h>

#include <libtardy/tar/input/ar/pdp11.h>
#include <libtardy/tar/input/filter/ar_long_names.h>
#include <libtardy/tar/input/filter/ar_long_names2.h>
#include <libtardy/tar/output/ar/pdp11.h>


tar_input_ar_pdp11::~tar_input_ar_pdp11()
{
}


tar_input_ar_pdp11::tar_input_ar_pdp11(
    const file_input::pointer &a_ifp,
    endian_t a_endian
) :
    tar_input_ar(a_ifp, 2),
    endian(a_endian)
{
}


tar_input_ar_pdp11::pointer
tar_input_ar_pdp11::create(const file_input::pointer &a_ifp,
    endian_t a_endian)
{
    pointer p(new tar_input_ar_pdp11(a_ifp, a_endian));
    p = tar_input_filter_ar_long_names2::create(p);
    return tar_input_filter_ar_long_names::create(p);
}


static const unsigned MAGIC = 0177555;


tar_input_ar_pdp11::pointer
tar_input_ar_pdp11::create(const file_input::pointer &a_ifp)
{
    endian_t a_endian = endian_little;
    unsigned char buffer[2];
    if
    (
        a_ifp->peek(buffer, sizeof(buffer)) == sizeof(buffer)
    &&
        endian_get2be(buffer) == MAGIC
    )
        a_endian = endian_big;
    return create(a_ifp, a_endian);
}


bool
tar_input_ar_pdp11::candidate(const file_input::pointer &a_ifp)
{
    unsigned char buffer[2];
    return
        (
            2 == a_ifp->peek(buffer, 2)
        &&
            (endian_get2le(buffer) == MAGIC || endian_get2be(buffer) == MAGIC)
        );
}


tar_input_ar_pdp11::pointer
tar_input_ar_pdp11::create_le(const file_input::pointer &a_ifp)
{
    return create(a_ifp, endian_little);
}


tar_input_ar_pdp11::pointer
tar_input_ar_pdp11::create_be(const file_input::pointer &a_ifp)
{
    return create(a_ifp, endian_big);
}


const char *
tar_input_ar_pdp11::get_format_name(void)
    const
{
    return (endian == endian_little ? "ar-pdp11-le" : "ar-pdp11-be");
}


void
tar_input_ar_pdp11::read_archive_begin(void)
{
    unsigned char buffer[2];
    if (read_deeper(buffer, sizeof(buffer)) != sizeof(buffer))
        fatal("short magic number read");
    unsigned n = get2(buffer);
    if (n != MAGIC)
        fatal("bad magic number (expected %#o, read %#o)", MAGIC, n);
}


bool
tar_input_ar_pdp11::read_header(tar_header &hdr)
{
    //
    // Name Offset Size
    // ---- ------ ----
    // name      0    8
    // date      8    4
    // uid      12    1
    // gid      13    1
    // mode     14    2
    // size     16    2
    // ---- ------ ----
    // Total:        18
    //
    unsigned char buffer[18];
    size_t nbytes = read_deeper(buffer, sizeof(buffer));
    if (nbytes == 0)
        return false;
    if (nbytes != sizeof(buffer))
        fatal("short header read");
    unsigned char *end = (unsigned char *)memchr(buffer, '\0', 8);
    if (!end)
        end = buffer + 8;
    hdr.name = rcstring((char *)buffer, end - buffer);
    hdr.type = tar_header::type_normal;
    hdr.mtime = get4(buffer + 8);
    hdr.user_id = buffer[12];
    hdr.group_id = buffer[13];
    hdr.mode = get2(buffer + 14);
    hdr.size = get2(buffer + 16);
    return true;
}


unsigned
tar_input_ar_pdp11::get2(unsigned char *data)
    const
{
    return endian_get2(data, endian);
}


unsigned long
tar_input_ar_pdp11::get4(unsigned char *data)
    const
{
    unsigned n1 = get2(data);
    unsigned n2 = get2(data + 2);
    return (((unsigned long)n1 << 16) | n2);
}


tar_output::pointer
tar_input_ar_pdp11::tar_output_factory(const file_output::pointer &ofp)
    const
{
    return tar_output_ar_pdp11::create(ofp, endian);
}


size_t
tar_input_ar_pdp11::get_maximum_name_length(void)
    const
{
    return 8;
}
