//
// 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/errno.h>
#include <libtardy/ac/fcntl.h>
#include <libtardy/ac/sys/param.h>
#include <libtardy/ac/sys/stat.h>
#include <libexplain/close.h>
#include <libexplain/creat.h>
#include <libexplain/mkdir.h>
#include <libexplain/write.h>

#include <libtardy/tar/output/extract.h>


tar_output_extract::~tar_output_extract()
{
}


tar_output_extract::tar_output_extract() :
    fd(-1)
{
}


tar_output_extract::pointer
tar_output_extract::create(void)
{
    return pointer(new tar_output_extract());
}


void
tar_output_extract::write_header(const tar_header &hdr)
{
    assert(fd == -1);
    current_header = hdr;
}


static void
mkdir_p(const rcstring &path)
{
    if (mkdir(path.c_str(), 0755) >= 0)
        return;
    if (errno != ENOENT)
        return;
    mkdir_p(path.dirname());
    mkdir(path.c_str(), 0755);
}


void
tar_output_extract::write_header_padding(void)
{
    assert(fd == -1);
    mkdir_p(current_header.name.dirname());
    switch (current_header.type)
    {
    case tar_header::type_device_block:
        fatal
        (
            "not creating block special device %s",
            current_header.name.quote_c().c_str()
        );
        break;

    case tar_header::type_device_character:
        fatal
        (
            "not creating character special device %s",
            current_header.name.quote_c().c_str()
        );
        break;

    case tar_header::type_directory:
        explain_mkdir_or_die(current_header.name.c_str(), current_header.mode);
        break;

    case tar_header::type_fifo:
        fatal
        (
            "not creating fifo %s",
            current_header.name.quote_c().c_str()
        );
        break;

    case tar_header::type_link_hard:
        fatal
        (
            "not creating hard link %s",
            current_header.name.quote_c().c_str()
        );
        break;

    case tar_header::type_link_symbolic:
        fatal
        (
            "not creating symbolic link %s",
            current_header.name.quote_c().c_str()
        );
        break;

    case tar_header::type_normal:
    case tar_header::type_normal_contiguous:
        fd =
            explain_creat_or_die
            (
                current_header.name.c_str(),
                current_header.mode
            );
        break;

    case tar_header::type_normal_gzipped:
        assert(!"not implemented yet");
        break;

    case tar_header::type_socket:
        fatal
        (
            "not creating socket %s",
            current_header.name.quote_c().c_str()
        );
        break;

    default:
        assert(!"these aren't the droids you are looking for");
        break;
    }
}


void
tar_output_extract::write_data(void const *data, int data_size)
{
    assert(fd >= 0);
    ssize_t nbytes = explain_write_or_die(fd, data, data_size);
    if (nbytes != data_size)
        fatal("short write (expected %d, got %ld)", data_size, (long)nbytes);
}


void
tar_output_extract::write_data_padding(void)
{
    switch (current_header.type)
    {
    case tar_header::type_device_block:
    case tar_header::type_device_character:
    case tar_header::type_directory:
    case tar_header::type_fifo:
    case tar_header::type_link_hard:
    case tar_header::type_link_symbolic:
    case tar_header::type_socket:
        assert(fd == -1);
        break;

    case tar_header::type_normal:
    case tar_header::type_normal_contiguous:
        assert(fd >= 0);
        explain_close_or_die(fd);
        fd = -1;
        break;

    case tar_header::type_normal_gzipped:
        assert(!"not implemented yet");
        break;
    }
}


rcstring
tar_output_extract::filename(void)
    const
{
    return "extracting";
}


const char *
tar_output_extract::get_format_name(void)
    const
{
    return "extract";
}


size_t
tar_output_extract::get_maximum_name_length(void)
    const
{
    return PATH_MAX;
}
