//
// 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/v7.h>
#include <libtardy/tar/input/filter/ar_long_names.h>
#include <libtardy/tar/input/filter/ar_long_names2.h>
#include <libtardy/tar/output/ar/v7.h>


tar_input_ar_v7::~tar_input_ar_v7()
{
}


tar_input_ar_v7::tar_input_ar_v7(
    const file_input::pointer &a_ifp,
    endian_t a_endian
) :
    tar_input_ar(a_ifp, 0),
    endian(a_endian)
{
}


tar_input_ar_v7::pointer
tar_input_ar_v7::create(const file_input::pointer &a_ifp, endian_t a_endian)
{
    pointer p(new tar_input_ar_v7(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 = 0177545;


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


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


tar_input_ar_v7::pointer
tar_input_ar_v7::create_le(const file_input::pointer &a_ifp)
{
    // version 7 was developed on a PDP11, which is little-endian.
    return create(a_ifp, endian_little);
}


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


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


void
tar_input_ar_v7::read_archive_begin(void)
{
    unsigned char buffer[2];
    if (2 != read_deeper(buffer, 2))
        fatal("short magic number read");
    if (get2(buffer) != MAGIC)
        fatal("bad magic number");
}


bool
tar_input_ar_v7::read_header(tar_header &hdr)
{
    //
    // Name Offset Size
    // ---- ------ ----
    // name      0   14
    // date     14    4    // long in byte order 2 3 1 0
    // uid      18    1
    // gid      19    1
    // mode     20    2    // short in byte order 0 1
    // size     22    4    // long in byte order 2 3 1 0
    // ---- ------ ----
    // Total:        26
    //
    unsigned char buffer[26];
    size_t nbytes = read_deeper(buffer, sizeof(buffer));
    if (nbytes == 0)
        return false;
    if (nbytes != sizeof(buffer))
        fatal("short header read");
    unsigned char *ep = (unsigned char *)memchr(buffer, '\0', 14);
    size_t name_size = (ep ? ep - buffer : 14);
    hdr.name = rcstring((char *)buffer, name_size);
    hdr.mtime = get4(buffer + 14);
    hdr.user_id = buffer[18];
    hdr.group_id = buffer[19];
    hdr.mode = get2(buffer + 20);
    hdr.type = tar_header::type_normal;
    hdr.size = get4(buffer + 22);
    return true;
}


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


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


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


size_t
tar_input_ar_v7::get_maximum_name_length(void)
    const
{
    return 14;
}
