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


tar_output_ar_v7::~tar_output_ar_v7()
{
}


tar_output_ar_v7::tar_output_ar_v7(
    const file_output::pointer &a_ofp,
    endian_t a_endian
) :
    tar_output_ar(a_ofp, 0),
    endian(a_endian)
{
}


tar_output_ar_v7::pointer
tar_output_ar_v7::create(const file_output::pointer &a_ofp, endian_t a_endian)
{
    pointer p(new tar_output_ar_v7(a_ofp, a_endian));
    p = tar_output_filter_ar_long_names::create(p);
    return tar_output_filter_basename::create(p);
}


tar_output_ar_v7::pointer
tar_output_ar_v7::create_le(const file_output::pointer &a_ofp)
{
    return create(a_ofp, endian_little);
}


tar_output_ar_v7::pointer
tar_output_ar_v7::create_be(const file_output::pointer &a_ofp)
{
    return create(a_ofp, endian_big);
}


void
tar_output_ar_v7::write_archive_begin(void)
{
    const int ARMAG = 0177545;
    unsigned char buffer[2];
    put2(buffer, ARMAG);
    write_deeper(buffer, 2);
}


#define min(a, b) ((a) < (b) ? (a) : (b))


void
tar_output_ar_v7::write_header(const tar_header &hdr)
{
    switch (hdr.type)
    {
    case tar_header::type_device_block:
    case tar_header::type_device_character:
    case tar_header::type_fifo:
    case tar_header::type_link_hard:
    case tar_header::type_link_symbolic:
    case tar_header::type_normal_gzipped:
    case tar_header::type_socket:
        fatal("%s: file type not supported", hdr.name.c_str());
        break;

    case tar_header::type_directory:
        break;

    case tar_header::type_normal:
    case tar_header::type_normal_contiguous:
        //
        // 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 name_size = hdr.name.size();
        if (name_size > 14)
            name_size = 14;
        memcpy(buffer, hdr.name.c_str(), name_size);
        memset(buffer + name_size, 0, 14 - name_size);
        put4(buffer + 14, hdr.mtime);
        buffer[18] = min(255, hdr.user_id);
        buffer[19] = min(255, hdr.group_id);
        put2(buffer + 20, hdr.mode);
        put4(buffer + 22, hdr.size);
        write_deeper(buffer, sizeof(buffer));
        break;
    }
}


void
tar_output_ar_v7::put2(unsigned char *data, unsigned value)
    const
{
    endian_set2(data, value, endian);
}


void
tar_output_ar_v7::put4(unsigned char *data, unsigned long value)
    const
{
    put2(data, value >> 16);
    put2(data + 2, value);
}


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


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