ObjFW  Check-in [790b5344d6]

Overview
Comment:Add OFLHAArchive

This is currently limited to reading uncompressed LHA level 2 files.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 790b5344d648625392b2f35f9cb4b3670c878ecc055e39ee13f698be34a2d267
User & Date: js on 2018-05-26 23:04:24
Other Links: manifest | tags
Context
2018-05-27
00:21
OFLHAArchiveEntry: Parse more extensions check-in: 56d02a1ac5 user: js tags: trunk
2018-05-26
23:04
Add OFLHAArchive check-in: 790b5344d6 user: js tags: trunk
15:22
Work around amiga-gcc not having trunc() check-in: 15a59cf9d5 user: js tags: trunk
Changes

Modified src/Makefile from [0390ef4ea5] to [70ee2f036d].

31
32
33
34
35
36
37


38
39
40
41
42
43
44
       OFHTTPCookieManager.m		\
       OFHTTPRequest.m			\
       OFHTTPResponse.m			\
       OFInflate64Stream.m		\
       OFInflateStream.m		\
       OFIntrospection.m		\
       OFInvocation.m			\


       OFList.m				\
       OFLocalization.m			\
       OFMapTable.m			\
       OFMD5Hash.m			\
       OFMessagePackExtension.m		\
       OFMethodSignature.m		\
       OFMutableArray.m			\







>
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
       OFHTTPCookieManager.m		\
       OFHTTPRequest.m			\
       OFHTTPResponse.m			\
       OFInflate64Stream.m		\
       OFInflateStream.m		\
       OFIntrospection.m		\
       OFInvocation.m			\
       OFLHAArchive.m			\
       OFLHAArchiveEntry.m		\
       OFList.m				\
       OFLocalization.m			\
       OFMapTable.m			\
       OFMD5Hash.m			\
       OFMessagePackExtension.m		\
       OFMethodSignature.m		\
       OFMutableArray.m			\

Added src/OFLHAArchive.h version [874facd305].

















































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018
 *   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.
 */

#import "OFObject.h"
#import "OFKernelEventObserver.h"
#import "OFLHAArchiveEntry.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

@class OFStream;

/*!
 * @class OFLHAArchive OFLHAArchive.h ObjFW/OFLHAArchive.h
 *
 * @brief A class for accessing and manipulating LHA files.
 */
@interface OFLHAArchive: OFObject
{
	OF_KINDOF(OFStream *) _stream;
	of_string_encoding_t _encoding;
	OF_KINDOF(OFStream *) _Nullable _lastReturnedStream;
}

/*!
 * @brief The encoding to use for the archive. Defaults to ISO 8859-1.
 */
@property (nonatomic) of_string_encoding_t encoding;

/*!
 * @brief A stream for reading the current entry.
 *
 * @note This is only available in read mode.
 *
 * @note The returned stream only conforms to @ref OFReadyForReadingObserving if
 *	 the underlying stream does so, too.
 */
@property (readonly, nonatomic)
    OFStream <OFReadyForReadingObserving> *streamForReadingCurrentEntry;

/*!
 * @brief Creates a new OFLHAArchive object with the specified stream.
 *
 * @param stream A stream from which the LHA archive will be read.
 *		 For read and append mode, this needs to be an OFSeekableStream.
 * @param mode The mode for the LHA file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFLHAArchive
 */
+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode;

#ifdef OF_HAVE_FILES
/*!
 * @brief Creates a new OFLHAArchive object with the specified file.
 *
 * @param path The path to the LHA file
 * @param mode The mode for the LHA file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFLHAArchive
 */
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode;
#endif

- (instancetype)init OF_UNAVAILABLE;

/*!
 * @brief Initializes an already allocated OFLHAArchive object with the
 *	  specified stream.
 *
 * @param stream A stream from which the LHA archive will be read.
 *		 For read and append mode, this needs to be an OFSeekableStream.
 * @param mode The mode for the LHA file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return An initialized OFLHAArchive
 */
- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;

#ifdef OF_HAVE_FILES
/*!
 * @brief Initializes an already allocated OFLHAArchive object with the
 *	  specified file.
 *
 * @param path The path to the LHA file
 * @param mode The mode for the LHA file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return An initialized OFLHAArchive
 */
- (instancetype)initWithPath: (OFString *)path
			mode: (OFString *)mode;
#endif

/*!
 * @brief Returns the next entry from the LHA archive or `nil` if all entries
 *	  have been read.
 *
 * @note This is only available in read mode.
 *
 * @warning Calling @ref nextEntry will invalidate all streams returned by
 *	    @ref streamForReadingCurrentEntry or
 *	    @ref streamForWritingEntry:! Reading from or writing to an
 *	    invalidated stream will throw an @ref OFReadFailedException or
 *	    @ref OFWriteFailedException!
 *
 * @return The next entry from the LHA archive or `nil` if all entries have
 *	   been read
 */
- (nullable OFLHAArchiveEntry *)nextEntry;

/*!
 * @brief Closes the OFLHAArchive.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END

Added src/OFLHAArchive.m version [d7dfa4dbcd].































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018
 *   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.
 */

#include "config.h"

#import "OFLHAArchive.h"
#import "OFLHAArchiveEntry.h"
#import "OFLHAArchiveEntry+Private.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFStream.h"
#import "OFSeekableStream.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"

@interface OFLHAArchive_FileReadStream: OFStream <OFReadyForReadingObserving>
{
	OFLHAArchiveEntry *_entry;
	OF_KINDOF(OFStream *) _stream;
	uint32_t _toRead;
	bool _atEndOfStream;
}

- (instancetype)of_initWithStream: (OFStream *)stream
			    entry: (OFLHAArchiveEntry *)entry;
- (void)of_skip;
@end

@implementation OFLHAArchive
@synthesize encoding = _encoding;

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
{
	return [[[self alloc] initWithStream: stream
					mode: mode] autorelease];
}

#ifdef OF_HAVE_FILES
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode
{
	return [[[self alloc] initWithPath: path
				      mode: mode] autorelease];
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
{
	self = [super init];

	@try {
		if (![mode isEqual: @"r"])
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];

		_stream = [stream retain];
		_encoding = OF_STRING_ENCODING_ISO_8859_1;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

#ifdef OF_HAVE_FILES
- (instancetype)initWithPath: (OFString *)path
			mode: (OFString *)mode
{
	OFFile *file;

	if ([mode isEqual: @"a"])
		file = [[OFFile alloc] initWithPath: path
					       mode: @"r+"];
	else
		file = [[OFFile alloc] initWithPath: path
					       mode: mode];

	@try {
		self = [self initWithStream: file
				       mode: mode];
	} @finally {
		[file release];
	}

	return self;
}
#endif

- (void)dealloc
{
	[self close];

	[super dealloc];
}

- (OFLHAArchiveEntry *)nextEntry
{
	OFLHAArchiveEntry *entry;
	uint8_t headerSize;

	[_lastReturnedStream of_skip];
	[_lastReturnedStream close];
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	if ([_stream isAtEndOfStream])
		return nil;

	if ([_stream readIntoBuffer: &headerSize
			     length: 1] == 0)
		return nil;

	if (headerSize == 0)
		return nil;

	entry = [[[OFLHAArchiveEntry alloc]
	    of_initWithHeaderSize: headerSize
			   stream: _stream
			 encoding: _encoding] autorelease];

	_lastReturnedStream = [[OFLHAArchive_FileReadStream alloc]
	    of_initWithStream: _stream
			entry: entry];

	return entry;
}

- (OFStream <OFReadyForReadingObserving> *)streamForReadingCurrentEntry
{
	if (_lastReturnedStream == nil)
		@throw [OFInvalidArgumentException exception];

	return [[_lastReturnedStream retain] autorelease];
}

- (void)close
{
	if (_stream == nil)
		return;

	[_lastReturnedStream close];
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	[_stream release];
	_stream = nil;
}
@end

@implementation OFLHAArchive_FileReadStream
- (instancetype)of_initWithStream: (OFStream *)stream
			    entry: (OFLHAArchiveEntry *)entry
{
	self = [super init];

	@try {
		if ([entry method] != OF_LHA_ARCHIVE_ENTRY_METHOD_LH0 &&
		    [entry method] != OF_LHA_ARCHIVE_ENTRY_METHOD_LHD)
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];

		_entry = [entry copy];
		_stream = [stream retain];
		_toRead = [entry uncompressedSize];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[self close];

	[_entry release];

	[super dealloc];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	size_t ret;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_atEndOfStream)
		return 0;

	if (length > _toRead)
		length = _toRead;

	ret = [_stream readIntoBuffer: buffer
			       length: length];

	if (ret == 0)
		_atEndOfStream = true;

	_toRead -= ret;

	return ret;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (bool)hasDataInReadBuffer
{
	return ([super hasDataInReadBuffer] || [_stream hasDataInReadBuffer]);
}

- (int)fileDescriptorForReading
{
	return [_stream fileDescriptorForReading];
}

- (void)close
{
	[self of_skip];

	[_stream release];
	_stream = nil;

	[super close];
}

- (void)of_skip
{
	if (_stream == nil || _toRead == 0)
		return;

	if ([_stream isKindOfClass: [OFSeekableStream class]] &&
	    (sizeof(of_offset_t) > 4 || _toRead < INT32_MAX)) {
		[_stream seekToOffset: (of_offset_t)_toRead
			       whence: SEEK_CUR];

		_toRead = 0;
	} else {
		while (_toRead > 0) {
			char buffer[512];
			size_t min = _toRead;

			if (min > 512)
				min = 512;

			_toRead -= [_stream readIntoBuffer: buffer
						    length: min];
		}
	}
}
@end

Added src/OFLHAArchiveEntry+Private.h version [2c161f93e6].



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018
 *   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.
 */

#import "OFLHAArchive.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFLHAArchiveEntry ()
- (instancetype)of_initWithHeaderSize: (uint8_t)headerSize
			       stream: (OFStream *)stream
			     encoding: (of_string_encoding_t)encoding
    OF_METHOD_FAMILY(init);
@end

OF_ASSUME_NONNULL_END

Added src/OFLHAArchiveEntry.h version [a130af009d].











































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018
 *   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.
 */

#import "OFObject.h"

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFData;
@class OFDate;

/*! @file */

/*!
 * @brief The compression method of the archive entry.
 */
typedef enum of_lha_archive_method_t {
	/*! No compression */
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH0,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LZS,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LZ4,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH1,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH2,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH3,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH4,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH5,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH6,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH7,
	OF_LHA_ARCHIVE_ENTRY_METHOD_LH8,
	/*! Directory */
	OF_LHA_ARCHIVE_ENTRY_METHOD_LHD
} of_lha_archive_method_t;

/*!
 * @class OFLHAArchiveEntry OFLHAArchiveEntry.h ObjFW/OFLHAArchiveEntry.h
 *
 * @brief A class which represents an entry in the central directory of a LHA
 *	  archive.
 */
@interface OFLHAArchiveEntry: OFObject <OFCopying>
{
#ifdef OF_LHA_ARCHIVE_ENTRY_M
@public
#endif
	of_lha_archive_method_t _method;
	OFString *_fileName, *_directoryName;
	uint32_t _compressedSize, _uncompressedSize;
	OFDate *_date;
	uint8_t _level;
	uint16_t _CRC16;
	uint8_t _operatingSystemIdentifier;
	OFArray OF_GENERIC(OFData *) *_extensions;
}

/*!
 * @brief The method of the entry.
 */
@property (readonly, nonatomic) of_lha_archive_method_t method;

/*!
 * @brief The file name of the entry.
 */
@property (readonly, copy, nonatomic) OFString *fileName;

/*!
 * @brief The compressed size of the entry's file.
 */
@property (readonly, nonatomic) uint32_t compressedSize;

/*!
 * @brief The uncompressed size of the entry's file.
 */
@property (readonly, nonatomic) uint32_t uncompressedSize;

/*!
 * @brief The date of the file.
 */
@property (readonly, retain, nonatomic) OFDate *date;

/*!
 * @brief The LHA level.
 */
@property (readonly, nonatomic) uint8_t level;

/*!
 * @brief The CRC16 of the file.
 */
@property (readonly, nonatomic) uint16_t CRC16;

/*!
 * @brief The operating system identifier.
 */
@property (readonly, nonatomic) uint8_t operatingSystemIdentifier;

/*!
 * @brief The LHA extensions of the file.
 */
@property (readonly, copy, nonatomic) OFArray OF_GENERIC(OFData *) *extensions;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Added src/OFLHAArchiveEntry.m version [70e652d7fb].

















































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018
 *   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.
 */

#include "config.h"

#define OF_LHA_ARCHIVE_ENTRY_M

#import "OFLHAArchiveEntry.h"
#import "OFLHAArchiveEntry+Private.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFStream.h"
#import "OFString.h"

#import "OFInvalidFormatException.h"
#import "OFUnsupportedVersionException.h"

static void
parseFileNameExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding)
{
	[entry->_fileName release];
	entry->_fileName = nil;

	entry->_fileName = [[OFString alloc]
	    initWithCString: (char *)[extension items] + 1
		   encoding: encoding
		     length: [extension count] - 1];
}

static void
parseDirectoryNameExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding)
{
	void *pool = objc_autoreleasePoolPush();
	OFString *tmp = [OFString
	    stringWithCString: (char *)[extension items] + 1
		     encoding: encoding
		       length: [extension count] - 1];
	OFString *separator = [OFString stringWithCString: "\xFF"
						 encoding: encoding
						   length: 1];

	if (![tmp hasSuffix: separator])
		@throw [OFInvalidFormatException exception];

	tmp = [tmp stringByReplacingOccurrencesOfString: separator
					     withString: @"/"];

	[entry->_directoryName release];
	entry->_directoryName = nil;

	entry->_directoryName = [tmp copy];

	objc_autoreleasePoolPop(pool);
}

static void
parseExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding)
{
	switch (*(char *)[extension itemAtIndex: 0]) {
	case 0x01:
		parseFileNameExtension(entry, extension, encoding);
		break;
	case 0x02:
		parseDirectoryNameExtension(entry, extension, encoding);
		break;
	}
}

@implementation OFLHAArchiveEntry
@synthesize method = _method, compressedSize = _compressedSize;
@synthesize uncompressedSize = _uncompressedSize, date = _date;
@synthesize level = _level, CRC16 = _CRC16;
@synthesize operatingSystemIdentifier = _operatingSystemIdentifier;
@synthesize extensions = _extensions;

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithHeaderSize: (uint8_t)headerSize
			       stream: (OFStream *)stream
			     encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		char header[20];
		uint32_t date;
		uint16_t nextSize;
		OFMutableArray *extensions;

		if (headerSize < 21)
			@throw [OFInvalidFormatException exception];

		[stream readIntoBuffer: header
			   exactLength: 20];

		if (memcmp(header + 1, "-lh0-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH0;
		else if (memcmp(header + 1, "-lzs-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LZS;
		else if (memcmp(header + 1, "-lz4-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LZ4;
		else if (memcmp(header + 1, "-lh1-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH1;
		else if (memcmp(header + 1, "-lh2-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH2;
		else if (memcmp(header + 1, "-lh3-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH3;
		else if (memcmp(header + 1, "-lh4-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH4;
		else if (memcmp(header + 1, "-lh5-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH5;
		else if (memcmp(header + 1, "-lh6-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH6;
		else if (memcmp(header + 1, "-lh7-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH7;
		else if (memcmp(header + 1, "-lh8-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH8;
		else if (memcmp(header + 1, "-lhd-", 5) == 0)
			_method = OF_LHA_ARCHIVE_ENTRY_METHOD_LHD;
		else {
			OFString *version = [OFString
			    stringWithCString: header + 1
				     encoding: OF_STRING_ENCODING_ASCII
				       length: 5];

			@throw [OFUnsupportedVersionException
			    exceptionWithVersion: version];
		}

		memcpy(&_compressedSize, header + 6, 4);
		_compressedSize = OF_BSWAP32_IF_BE(_compressedSize);

		memcpy(&_uncompressedSize, header + 10, 4);
		_uncompressedSize = OF_BSWAP32_IF_BE(_uncompressedSize);

		memcpy(&date, header + 14, 4);
		date = OF_BSWAP32_IF_BE(date);

		_level = header[19];

		if (_level != 2) {
			OFString *version = [OFString
			    stringWithFormat: @"%u", _level];

			@throw [OFUnsupportedVersionException
			    exceptionWithVersion: version];
		}

		_date = [[OFDate alloc] initWithTimeIntervalSince1970: date];

		_CRC16 = [stream readLittleEndianInt16];
		_operatingSystemIdentifier = [stream readInt8];

		extensions = [[OFMutableArray alloc] init];
		_extensions = extensions;

		while ((nextSize = [stream readLittleEndianInt16]) > 0) {
			OFData *extension;

			if (nextSize < 2)
				@throw [OFInvalidFormatException exception];

			extension = [stream readDataWithCount: nextSize - 2];
			[extensions addObject: extension];

			parseExtension(self, extension, encoding);
		}

		[extensions makeImmutable];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_fileName release];
	[_directoryName release];
	[_date release];
	[_extensions release];

	[super dealloc];
}

- (id)copy
{
	return [self retain];
}

- (OFString *)fileName
{
	if (_directoryName == nil)
		return _fileName;

	return [_directoryName stringByAppendingString: _fileName];
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	OFString *extensions = [[_extensions description]
	    stringByReplacingOccurrencesOfString: @"\n"
				      withString: @"\n\t"];
	OFString *ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tFile name = %@\n"
	    @"\tCompressed size = %" @PRIu32 "\n"
	    @"\tUncompressed size = %" @PRIu32 "\n"
	    @"\tDate = %@\n"
	    @"\tLevel = %u\n"
	    @"\tCRC16 = %04" @PRIX16 @"\n"
	    @"\tOperating system identifier = %c\n"
	    @"\tExtensions: %@"
	    @">",
	    [self class], [self fileName], _compressedSize, _uncompressedSize,
	    _date, _level, _CRC16, _operatingSystemIdentifier, extensions];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

Modified src/OFTarArchive.h from [f2708a375a] to [1091af78e2].

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
		OF_TAR_ARCHIVE_MODE_APPEND
	} _mode;
	of_string_encoding_t _encoding;
	OF_KINDOF(OFStream *) _Nullable _lastReturnedStream;
}

/*!
 * @brief The encoding to use for the archive.
 */
@property (nonatomic) of_string_encoding_t encoding;

/*!
 * @brief A stream for reading the current entry
 *
 * @note This is only available in read mode.
 *
 * @note The returned stream only conforms to @ref OFReadyForReadingObserving if
 *	 the underlying stream does so, too.
 */
@property (readonly, nonatomic)







|




|







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
		OF_TAR_ARCHIVE_MODE_APPEND
	} _mode;
	of_string_encoding_t _encoding;
	OF_KINDOF(OFStream *) _Nullable _lastReturnedStream;
}

/*!
 * @brief The encoding to use for the archive. Defaults to UTF-8.
 */
@property (nonatomic) of_string_encoding_t encoding;

/*!
 * @brief A stream for reading the current entry.
 *
 * @note This is only available in read mode.
 *
 * @note The returned stream only conforms to @ref OFReadyForReadingObserving if
 *	 the underlying stream does so, too.
 */
@property (readonly, nonatomic)
79
80
81
82
83
84
85


86
87
88
89
90
91
92
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFTarArchive
 */
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode;
#endif



/*!
 * @brief Initializes an already allocated OFTarArchive object with the
 *	  specified stream.
 *
 * @param stream A stream from which the tar archive will be read.
 *		 For append mode, this needs to be an OFSeekableStream.







>
>







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFTarArchive
 */
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode;
#endif

- (instancetype)init OF_UNAVAILABLE;

/*!
 * @brief Initializes an already allocated OFTarArchive object with the
 *	  specified stream.
 *
 * @param stream A stream from which the tar archive will be read.
 *		 For append mode, this needs to be an OFSeekableStream.

Modified src/OFTarArchive.m from [969975ba2f] to [d5ac892200].

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78





79
80
81
82
83
84
85
{
	OFTarArchiveEntry *_entry;
	OF_KINDOF(OFStream *) _stream;
	uint64_t _toRead;
	bool _atEndOfStream;
}

- (instancetype)initWithStream: (OFStream *)stream
			 entry: (OFTarArchiveEntry *)entry;
- (void)of_skip;
@end

@interface OFTarArchive_FileWriteStream: OFStream <OFReadyForWritingObserving>
{
	OFTarArchiveEntry *_entry;
	OF_KINDOF(OFStream *) _stream;
	uint64_t _toWrite;
}

- (instancetype)initWithStream: (OFStream *)stream
			 entry: (OFTarArchiveEntry *)entry;
@end

@implementation OFTarArchive: OFObject
@synthesize encoding = _encoding;

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
{
	return [[[self alloc] initWithStream: stream
					mode: mode] autorelease];
}

#ifdef OF_HAVE_FILES
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode
{
	return [[[self alloc] initWithPath: path
				      mode: mode] autorelease];
}
#endif






- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
{
	self = [super init];

	@try {







|
|










|
|




















>
>
>
>
>







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
{
	OFTarArchiveEntry *_entry;
	OF_KINDOF(OFStream *) _stream;
	uint64_t _toRead;
	bool _atEndOfStream;
}

- (instancetype)of_initWithStream: (OFStream *)stream
			    entry: (OFTarArchiveEntry *)entry;
- (void)of_skip;
@end

@interface OFTarArchive_FileWriteStream: OFStream <OFReadyForWritingObserving>
{
	OFTarArchiveEntry *_entry;
	OF_KINDOF(OFStream *) _stream;
	uint64_t _toWrite;
}

- (instancetype)of_initWithStream: (OFStream *)stream
			    entry: (OFTarArchiveEntry *)entry;
@end

@implementation OFTarArchive: OFObject
@synthesize encoding = _encoding;

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
{
	return [[[self alloc] initWithStream: stream
					mode: mode] autorelease];
}

#ifdef OF_HAVE_FILES
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode
{
	return [[[self alloc] initWithPath: path
				      mode: mode] autorelease];
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
{
	self = [super init];

	@try {
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
	}

	entry = [[[OFTarArchiveEntry alloc]
	    of_initWithHeader: buffer.c
		     encoding: _encoding] autorelease];

	_lastReturnedStream = [[OFTarArchive_FileReadStream alloc]
	    initWithStream: _stream
		     entry: entry];

	return entry;
}

- (OFStream <OFReadyForReadingObserving> *)streamForReadingCurrentEntry
{
	if (_mode != OF_TAR_ARCHIVE_MODE_READ)







|
|







204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
	}

	entry = [[[OFTarArchiveEntry alloc]
	    of_initWithHeader: buffer.c
		     encoding: _encoding] autorelease];

	_lastReturnedStream = [[OFTarArchive_FileReadStream alloc]
	    of_initWithStream: _stream
			entry: entry];

	return entry;
}

- (OFStream <OFReadyForReadingObserving> *)streamForReadingCurrentEntry
{
	if (_mode != OF_TAR_ARCHIVE_MODE_READ)
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	[entry of_writeToStream: _stream
		       encoding: _encoding];

	_lastReturnedStream = [[OFTarArchive_FileWriteStream alloc]
	    initWithStream: _stream
		     entry: entry];

	objc_autoreleasePoolPop(pool);

	return [[_lastReturnedStream retain] autorelease];
}

- (void)close







|
|







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	[entry of_writeToStream: _stream
		       encoding: _encoding];

	_lastReturnedStream = [[OFTarArchive_FileWriteStream alloc]
	    of_initWithStream: _stream
			entry: entry];

	objc_autoreleasePoolPop(pool);

	return [[_lastReturnedStream retain] autorelease];
}

- (void)close
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

	[_stream release];
	_stream = nil;
}
@end

@implementation OFTarArchive_FileReadStream
- (instancetype)initWithStream: (OFStream *)stream
			 entry: (OFTarArchiveEntry *)entry
{
	self = [super init];

	@try {
		_entry = [entry copy];
		_stream = [stream retain];
		_toRead = [entry size];







|
|







271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

	[_stream release];
	_stream = nil;
}
@end

@implementation OFTarArchive_FileReadStream
- (instancetype)of_initWithStream: (OFStream *)stream
			    entry: (OFTarArchiveEntry *)entry
{
	self = [super init];

	@try {
		_entry = [entry copy];
		_stream = [stream retain];
		_toRead = [entry size];
342
343
344
345
346
347
348


349
350
351
352
353
354
355
356



357
358
359
360
361
362
363
- (int)fileDescriptorForReading
{
	return [_stream fileDescriptorForReading];
}

- (void)close
{


	[_stream release];
	_stream = nil;

	[super close];
}

- (void)of_skip
{



	if ([_stream isKindOfClass: [OFSeekableStream class]] &&
	    _toRead <= INT64_MAX && (of_offset_t)_toRead == (int64_t)_toRead) {
		uint64_t size;

		[_stream seekToOffset: (of_offset_t)_toRead
			       whence: SEEK_CUR];








>
>








>
>
>







347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
- (int)fileDescriptorForReading
{
	return [_stream fileDescriptorForReading];
}

- (void)close
{
	[self of_skip];

	[_stream release];
	_stream = nil;

	[super close];
}

- (void)of_skip
{
	if (_stream == nil || _toRead == 0)
		return;

	if ([_stream isKindOfClass: [OFSeekableStream class]] &&
	    _toRead <= INT64_MAX && (of_offset_t)_toRead == (int64_t)_toRead) {
		uint64_t size;

		[_stream seekToOffset: (of_offset_t)_toRead
			       whence: SEEK_CUR];

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
			[_stream readIntoBuffer: buffer
				    exactLength: (size_t)(512 - (size % 512))];
	}
}
@end

@implementation OFTarArchive_FileWriteStream
- (instancetype)initWithStream: (OFStream *)stream
			 entry: (OFTarArchiveEntry *)entry
{
	self = [super init];

	@try {
		_entry = [entry copy];
		_stream = [stream retain];
		_toWrite = [entry size];







|
|







400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
			[_stream readIntoBuffer: buffer
				    exactLength: (size_t)(512 - (size % 512))];
	}
}
@end

@implementation OFTarArchive_FileWriteStream
- (instancetype)of_initWithStream: (OFStream *)stream
			    entry: (OFTarArchiveEntry *)entry
{
	self = [super init];

	@try {
		_entry = [entry copy];
		_stream = [stream retain];
		_toWrite = [entry size];

Modified src/OFZIPArchiveEntry.m from [59110171a1] to [4035dadee9].

397
398
399
400
401
402
403
404

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
{
	return _localFileHeaderOffset;
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret = [OFString stringWithFormat: @"<%@:\n"

	    @"\tFile name = %@\n"
	    @"\tFile comment = %@\n"
	    @"\tGeneral purpose bit flag = %u\n"
	    @"\tCompression method = %u\n"
	    @"\tCompressed size = %ju\n"
	    @"\tUncompressed size = %ju\n"
	    @"\tModification date = %@\n"
	    @"\tCRC32 = %" @PRIu32 @"\n"
	    @"\tExtra field = %@\n"
	    @">",
	    [self class], _fileName, _fileComment, _generalPurposeBitFlag,
	    _compressionMethod, (intmax_t)_compressedSize,
	    (intmax_t)_uncompressedSize, [self modificationDate], _CRC32,
	    _extraField];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}







|
>




|
|

|



|
|
<







397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418

419
420
421
422
423
424
425
{
	return _localFileHeaderOffset;
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tFile name = %@\n"
	    @"\tFile comment = %@\n"
	    @"\tGeneral purpose bit flag = %u\n"
	    @"\tCompression method = %u\n"
	    @"\tCompressed size = %" @PRIu64 "\n"
	    @"\tUncompressed size = %" @PRIu64 "\n"
	    @"\tModification date = %@\n"
	    @"\tCRC32 = %08" @PRIX32 @"\n"
	    @"\tExtra field = %@\n"
	    @">",
	    [self class], _fileName, _fileComment, _generalPurposeBitFlag,
	    _compressionMethod, _compressedSize, _uncompressedSize,
	    [self modificationDate], _CRC32, _extraField];


	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

Modified src/ObjFW.h from [fc17987754] to [42d5f9fcae].

54
55
56
57
58
59
60


61
62
63
64
65
66
67
#import "OFColor.h"

#import "OFStream.h"
#import "OFStdIOStream.h"
#import "OFInflateStream.h"
#import "OFInflate64Stream.h"
#import "OFGZIPStream.h"


#import "OFTarArchive.h"
#import "OFTarArchiveEntry.h"
#import "OFZIPArchive.h"
#import "OFZIPArchiveEntry.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
# import "OFFileManager.h"







>
>







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#import "OFColor.h"

#import "OFStream.h"
#import "OFStdIOStream.h"
#import "OFInflateStream.h"
#import "OFInflate64Stream.h"
#import "OFGZIPStream.h"
#import "OFLHAArchive.h"
#import "OFLHAArchiveEntry.h"
#import "OFTarArchive.h"
#import "OFTarArchiveEntry.h"
#import "OFZIPArchive.h"
#import "OFZIPArchiveEntry.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
# import "OFFileManager.h"