//
// aegis - project change supervisor
// Copyright (C) 2002-2008 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
// .
//
#include // for assert
#include
#include
#include
#include
input_tar::~input_tar()
{
trace(("~input_tar()\n"));
}
input_tar::input_tar(input &arg) :
deeper(arg)
{
trace(("input_tar()\n"));
}
long
input_tar::read_inner(void *, size_t)
{
trace(("input_tar::read_inner()\n"));
assert(0);
return -1;
}
long
input_tar::ftell_inner()
{
trace(("input_tar::ftell_inner()\n"));
assert(0);
return 0;
}
nstring
input_tar::name()
{
return deeper->name();
}
long
input_tar::length()
{
trace(("input_tar::length()\n"));
return deeper->length();
}
static bool
all_zero(const char *buf, size_t len)
{
while (len-- > 0)
{
if (*buf++)
return false;
}
return true;
}
input
input_tar::child(nstring &archive_name, bool &executable)
{
//
// Wade through the garbage until we find something interesting.
//
trace(("input_tar::child()\n{\n"));
nstring longname;
for (;;)
{
//
// read the file header
//
char header[TBLOCK];
if
(
!deeper->read_strict(header, sizeof(header))
||
all_zero(header, sizeof(header))
)
{
trace(("return NULL\n"));
trace(("}\n"));
return 0;
}
header_ty *hp = (header_ty *)header;
//
// Get file mode
//
int mode = header_mode_get(hp);
executable = ((mode & 0111) != 0);
//
// Verify checksum.
//
int hchksum = header_checksum_get(hp);
if (hchksum < 0)
deeper->fatal_error("tar: corrupted checksum field");
int cs2 = header_checksum_calculate(hp);
if (hchksum != cs2)
{
header_dump(hp);
nstring s =
nstring::format
(
"tar: checksum does not match "
"(calculated 0%o, file has 0%o)",
cs2,
hchksum
);
deeper->fatal_error(s.c_str());
}
//
// The essential information we want from the header is the
// file's name and the file's size. All that other guff is ignored.
//
nstring aname;
if (!longname.empty())
{
aname = longname;
longname.clear();
}
else
{
aname = header_name_get(hp);
if (aname.empty())
deeper->fatal_error("tar: corrupted name field");
}
long hsize = header_size_get(hp);
if (hsize < 0)
deeper->fatal_error("tar: corrupted size field");
//
// Work out what to do with it.
//
switch (header_linkflag_get(hp))
{
case LF_OLDNORMAL:
case LF_NORMAL:
trace(("normal\n"));
if (aname[aname.size() - 1] == '/')
{
//
// Throw directories away. Aegis only likes real files.
//
continue;
}
break;
case LF_CONTIG:
trace(("contig\n"));
break;
case LF_LINK:
case LF_SYMLINK:
case LF_CHR:
case LF_BLK:
case LF_DIR:
case LF_FIFO:
//
// Throw these away. Aegis only likes real files.
//
trace(("throw this one away\n"));
continue;
case LF_LONGNAME:
//
// The next real file gets this_thing as its name.
//
trace(("longname\n"));
longname = read_data_as_string(hsize);
continue;
case LF_LONGLINK:
//
// The next file gets this_thing as its link name.
// (But we toss links, so toss the data).
//
trace(("longlink\n"));
deeper->skip(hsize);
padding();
continue;
default:
{
trace(("mystery file\n"));
nstring s =
nstring::format
(
"tar: file type \"%c\" unknown",
header_linkflag_get(hp)
);
deeper->fatal_error(s.c_str());
}
continue;
}
//
// Create a new input instance.
// the child will read everything.
//
trace(("real file\n"));
archive_name = aname;
long asize = (hsize + TBLOCK - 1) & ~(TBLOCK - 1);
input_crop *sub = 0;
if (asize == hsize)
{
sub = new input_crop(deeper, hsize);
}
else
{
input temp = new input_crop(deeper, asize);
sub = new input_crop(temp, hsize);
}
sub->set_name
(
nstring::format("%s(%s)", deeper->name().c_str(), aname.c_str())
);
trace(("return %08lX\n", (long)sub));
trace(("}\n"));
return sub;
}
}
void
input_tar::padding()
{
trace(("input_tar::padding()\n"));
int n = deeper->ftell();
n %= TBLOCK;
if (n)
deeper->skip(TBLOCK - n);
trace(("}\n"));
}
nstring
input_tar::read_data_as_string(size_t size)
{
trace(("input_tar::read_data_as_string(size = %ld)\n{\n", (long)size));
static char *strbuf;
static size_t maximum;
if (size > maximum)
{
for (;;)
{
maximum = maximum * 2 + 32;
if (maximum >= size)
break;
}
delete strbuf;
strbuf = new char [maximum];
}
deeper->read_strictest(strbuf, size);
while (size > 0 && strbuf[size - 1] == 0)
--size;
padding();
nstring result = nstring(strbuf, size);
trace(("return \"%s\";\n", result.c_str()));
trace(("}\n"));
return result;
}