// KDat - a tar-based DAT archiver
// Copyright (C) 1998-2000  Sean Vyain, svyain@mail.tds.net
// Copyright (C) 2001-2002  Lawrence Widman, kdat@cardiothink.com
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "TarParser.h"

#include "TarParser.moc"

TarParser::TarParser()
        : _bufIdx( 0 ),
          _blocksToSkip( 0 ),
          _record( 0 ),
          _longname( false ),
          _extended( false ),
          _archnameIdx( 0 )
{
}

void TarParser::slotData( const char * data, int length )
{
    for ( int i = 0; i < length; i++ ) {
        if ( _bufIdx >= 512 ) {
            if ( _blocksToSkip > 0 ) {
                if ( _extended ) {
                    record* rec = (record*)_buf;
                    _extended = rec->ext_hdr.isextended;
                } else {
                    _blocksToSkip--;
                    if ( _longname ) {
                        memcpy( _archname + _archnameIdx, _buf, 512 );
                        _archnameIdx += 512;
                    }
                }
            } else {
                parseTarBlock();
            }
            _bufIdx = 0;
            _record++;
        }
        _buf[_bufIdx++] = data[i];
    }
}

int TarParser::parseOctal( const char* buf, int length )
{
    int val = 0;
    
    for ( int i = 0; i < length; i++ ) {
        val *= 8;
        if ( ( buf[i] >= '1' ) && ( buf[i] <= '7' ) ) {
            val += buf[i] - '0';
        }
    }

    return val;
}

void TarParser::parseTarBlock()
{
    record* rec = (record*)_buf;

#if 0
    printf( "----- parsing tar block -----\n" );
    printf( "arch_name     = '%s'\n", rec->header.arch_name );
    printf( "mode          = '%s'\n", rec->header.mode );
    printf( "uid           = %d\n", parseOctal( rec->header.uid, 6 ) );
    printf( "gid           = %d\n", parseOctal( rec->header.gid, 6 ) );
    printf( "size          = %d\n", parseOctal( rec->header.size, 11 ) );
    printf( "mtime         = %d\n", parseOctal( rec->header.mtime, 11 ) );
    printf( "chksum        = '%s'\n", rec->header.chksum );
    printf( "linkflag      = '%c'\n", rec->header.linkflag );
    printf( "arch_linkname = '%s'\n", rec->header.arch_linkname );
    printf( "magic         = '%s'\n", rec->header.magic );
    printf( "uname         = '%s'\n", rec->header.uname );
    printf( "gname         = '%s'\n", rec->header.gname );
    printf( "devmajor      = %d\n", parseOctal( rec->header.devmajor, 8 ) );
    printf( "devminor      = %d\n", parseOctal( rec->header.devminor, 8 ) );
    printf( "atime         = %d\n", parseOctal( rec->header.atime, 11 ) );
    printf( "ctime         = %d\n", parseOctal( rec->header.ctime, 11 ) );
    printf( "offset        = %d\n", parseOctal( rec->header.offset, 11 ) );
    printf( "longnames     = '%s'\n", rec->header.longnames );
    printf( "isextended    = %d\n", rec->header.isextended );
    printf( "realsize      = %d\n", parseOctal( rec->header.realsize, 11 ) );
#endif

    if ( rec->header.magic[0] != 0 ) {
        _blocksToSkip = ( parseOctal( rec->header.size, 11 ) + 511 ) / 512;
        _extended = rec->header.isextended;

        if ( rec->header.linkflag == LF_LONGNAME ) {
            // The actual file name is stored in the next _blocksToSkip blocks of the tar-file.
            _longname = true;
            _archnameIdx = 0;
        } else {
            if ( _longname ) {
                _longname = false;
                emit sigEntry( _archname, parseOctal( rec->header.size, 11 ), parseOctal( rec->header.mtime, 11 ), _record );
            } else {
                emit sigEntry( rec->header.arch_name, parseOctal( rec->header.size, 11 ), parseOctal( rec->header.mtime, 11 ), _record );
            }
        }
    }
}
