/*
* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
* Jonathan Schleifer <js@heap.zone>
*
* All rights reserved.
*
* This file is part of ObjFW. It may be distributed under the terms of the
* Q Public License 1.0, which can be found in the file LICENSE.QPL included in
* the packaging of this file.
*
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#define OF_TAR_ARCHIVE_ENTRY_M
#include "config.h"
#import "OFTarArchiveEntry.h"
#import "OFTarArchiveEntry+Private.h"
#import "OFStream.h"
#import "OFDate.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
static OFString*
stringFromBuffer(const char *buffer, size_t length)
{
for (size_t i = 0; i < length; i++)
if (buffer[i] == '\0')
length = i;
return [OFString stringWithUTF8String: buffer
length: length];
}
static uintmax_t
octalValueFromBuffer(const char *buffer, size_t length, uintmax_t max)
{
uintmax_t value = [stringFromBuffer(buffer, length) octalValue];
if (value > max)
@throw [OFOutOfRangeException exception];
return value;
}
@implementation OFTarArchiveEntry
@synthesize fileName = _fileName, mode = _mode, size = _size;
@synthesize modificationDate = _modificationDate, type = _type;
@synthesize targetFileName = _targetFileName;
@synthesize owner = _owner, group = _group;
@synthesize deviceMajor = _deviceMajor, deviceMinor = _deviceMinor;
- (instancetype)OF_initWithHeader: (char[512])header
stream: (OFStream*)stream
{
self = [super init];
@try {
void *pool = objc_autoreleasePoolPush();
_stream = [stream retain];
_fileName = [stringFromBuffer(header, 100) copy];
_mode = (uint32_t)octalValueFromBuffer(
header + 100, 8, UINT32_MAX);
_size = _toRead = (uint64_t)octalValueFromBuffer(
header + 124, 12, UINT64_MAX);
_modificationDate = [[OFDate alloc]
initWithTimeIntervalSince1970:
(of_time_interval_t)octalValueFromBuffer(
header + 136, 12, UINTMAX_MAX)];
_type = header[156];
_targetFileName = [stringFromBuffer(header + 157, 100) copy];
if (_type == '\0')
_type = OF_TAR_ARCHIVE_ENTRY_TYPE_FILE;
if (memcmp(header + 257, "ustar\0" "00", 8) == 0) {
OFString *fileName;
_owner = [stringFromBuffer(header + 265, 32) copy];
_group = [stringFromBuffer(header + 297, 32) copy];
_deviceMajor = (uint32_t)octalValueFromBuffer(
header + 329, 8, UINT32_MAX);
_deviceMinor = (uint32_t)octalValueFromBuffer(
header + 337, 8, UINT32_MAX);
fileName = [OFString stringWithFormat: @"%@/%@",
stringFromBuffer(header + 345, 155), _fileName];
[_fileName release];
_fileName = [fileName copy];
}
objc_autoreleasePoolPop(pool);
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (void)dealloc
{
[_stream release];
[_fileName release];
[_modificationDate release];
[_targetFileName release];
[_owner release];
[_group release];
[super dealloc];
}
- (size_t)lowlevelReadIntoBuffer: (void*)buffer
length: (size_t)length
{
size_t ret;
if (_atEndOfStream)
@throw [OFReadFailedException exceptionWithObject: self
requestedLength: length];
if ((uint64_t)length > _toRead)
length = (size_t)_toRead;
ret = [_stream readIntoBuffer: buffer
length: length];
if (ret == 0)
_atEndOfStream = true;
_toRead -= ret;
return ret;
}
- (bool)lowlevelIsAtEndOfStream
{
return _atEndOfStream;
}
- (bool)hasDataInReadBuffer
{
return ([super hasDataInReadBuffer] || [_stream hasDataInReadBuffer]);
}
- (void)close
{
_atEndOfStream = true;
}
- (void)OF_skip
{
char buffer[512];
while (_toRead >= 512) {
[_stream readIntoBuffer: buffer
exactLength: 512];
_toRead -= 512;
}
if (_toRead > 0) {
[_stream readIntoBuffer: buffer
exactLength: (size_t)_toRead];
_toRead = 0;
}
if (_size % 512 != 0)
[_stream readIntoBuffer: buffer
exactLength: 512 - (_size % 512)];
}
@end