//
// 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/cpio/new_ascii.h>
#include <libtardy/tar/output/cpio/newascii.h>


tar_input_cpio_new_ascii::~tar_input_cpio_new_ascii()
{
}


tar_input_cpio_new_ascii::tar_input_cpio_new_ascii(
    const file_input::pointer &a_ifp
) :
    tar_input_cpio(a_ifp, 4)
{
}


tar_input_cpio_new_ascii::pointer
tar_input_cpio_new_ascii::create(const file_input::pointer &a_ifp)
{
    return pointer(new tar_input_cpio_new_ascii(a_ifp));
}


tar_output::pointer
tar_input_cpio_new_ascii::tar_output_factory(const file_output::pointer &ofp)
    const
{
    return tar_output_cpio_newascii::create(ofp);
}


bool
tar_input_cpio_new_ascii::candidate(const file_input::pointer &fp)
{
    char buffer[6];
    int nbytes = fp->peek(buffer, sizeof(buffer));
    return
        (
            nbytes == sizeof(buffer)
        &&
            rcstring(buffer, sizeof(buffer)) == "070701"
        );
}


bool
tar_input_cpio_new_ascii::read_header(tar_header &h)
{
    assert(sizeof(cpio_header) == 110);
    char buffer[sizeof(cpio_header)];
    size_t nbytes = read_deeper(buffer, 110);
    if (nbytes == 0)
        return false;
    if (nbytes != 110)
        fatal("short header read");
    cpio_header *hp = (cpio_header *)buffer;

    if (hp->get_magic() != "070701")
        fatal("bad header magic");
    h.device_major = hp->devmajor_get();
    h.device_minor = hp->devminor_get();
    h.group_id = hp->gid_get();
    h.inode_number = hp->ino_get();
    h.link_count = hp->nlink_get();
    unsigned pmode = hp->mode_get();
    h.mode = pmode & 07777;
    h.mtime = hp->mtime_get();
    h.rdevice_major = hp->rdevmajor_get();
    h.rdevice_minor = hp->rdevminor_get();
    h.size = hp->filesize_get();
    h.type = type_from_mode(pmode);
    h.user_id = hp->uid_get();
    unsigned name_size = hp->namesize_get();
    h.name = read_name(name_size);
    return !is_end(h);
}


rcstring
tar_input_cpio_new_ascii::cpio_header::get_magic(void)
    const
{
    return rcstring(magic, sizeof(magic));
}


long
tar_input_cpio_new_ascii::cpio_header::devmajor_get(void)
    const
{
    return octal(devmajor, sizeof(devmajor));
}


long
tar_input_cpio_new_ascii::cpio_header::devminor_get(void)
    const
{
    return octal(devminor, sizeof(devminor));
}


long
tar_input_cpio_new_ascii::cpio_header::gid_get(void)
    const
{
    return octal(gid, sizeof(gid));
}


long
tar_input_cpio_new_ascii::cpio_header::ino_get(void)
    const
{
    return octal(ino, sizeof(ino));
}


long
tar_input_cpio_new_ascii::cpio_header::nlink_get(void)
    const
{
    return octal(nlink, sizeof(nlink));
}


long
tar_input_cpio_new_ascii::cpio_header::mode_get(void)
    const
{
    return octal(mode, sizeof(mode));
}


long
tar_input_cpio_new_ascii::cpio_header::mtime_get(void)
    const
{
    return octal(mtime, sizeof(mtime));
}


long
tar_input_cpio_new_ascii::cpio_header::rdevmajor_get(void)
    const
{
    return octal(rdevmajor, sizeof(rdevmajor));
}


long
tar_input_cpio_new_ascii::cpio_header::rdevminor_get(void)
    const
{
    return octal(rdevminor, sizeof(rdevminor));
}


long
tar_input_cpio_new_ascii::cpio_header::filesize_get(void)
    const
{
    return octal(filesize, sizeof(filesize));
}


long
tar_input_cpio_new_ascii::cpio_header::uid_get(void)
    const
{
    return octal(uid, sizeof(uid));
}


long
tar_input_cpio_new_ascii::cpio_header::namesize_get(void)
    const
{
    return octal(namesize, sizeof(namesize));
}


long
tar_input_cpio_new_ascii::cpio_header::octal(const char *data, size_t data_size)
{
    long value = 0;
    while (data_size > 0 && *data == ' ')
    {
        ++data;
        --data_size;
    }
    if (data_size <= 0)
        return -1;
    while (data_size > 0)
    {
        if (!*data || *data == ' ')
            break;
        if (*data < '0' || *data > '7')
            return -1;
        //
        // Limit the range to 0..2^31-1.
        // Must test for overflow *before* the shift.
        //
        if (value & 0xF0000000)
        {
            return -1;
        }

        value = (value << 3) + (*data++ & 7);
        --data_size;
    }
    return value;
}


const char *
tar_input_cpio_new_ascii::get_format_name(void)
    const
{
    return "cpio-crc";
}


size_t
tar_input_cpio_new_ascii::get_maximum_name_length(void)
    const
{
    return ((size_t)1 << (3 * 8));
}
