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


tar_output_ar_pdp11::~tar_output_ar_pdp11()
{
}


tar_output_ar_pdp11::tar_output_ar_pdp11(
    const file_output::pointer &a_ofp,
    endian_t a_endian
) :
    tar_output_ar(a_ofp, 2),
    endian(a_endian)
{
}


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


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


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


static const unsigned MAGIC = 0177555;


void
tar_output_ar_pdp11::write_archive_begin(void)
{
    unsigned char buffer[2];
    put2(buffer, MAGIC);
    write_deeper(buffer, sizeof(buffer));
}


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


void
tar_output_ar_pdp11::write_header(const tar_header &hdr)
{
    switch (hdr.type)
    {
    default:
        fatal("%s: file type not supported", hdr.name.c_str());
        break;

    case tar_header::type_directory:
        // Silently ignore
        break;

    case tar_header::type_normal:
    case tar_header::type_normal_contiguous:
        //
        // 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 name_size = hdr.name.size();
        if (name_size > 8)
            name_size = 8;
        memcpy(buffer, hdr.name.c_str(), name_size);
        memset(buffer + name_size, 0, 8 - name_size);
        put4(buffer + 8, hdr.mtime);
        buffer[12] = min(255, hdr.user_id);
        buffer[13] = min(255, hdr.group_id);
        put2(buffer + 14, calculate_mode(hdr));
        if (hdr.size >= (1 << 16))
            fatal("%s: file too large", hdr.name.c_str());
        put2(buffer + 16, hdr.size);
        write_deeper(buffer, sizeof(buffer));
        break;
    }
}


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


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


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


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