ObjFW  Check-in [0c5035dd3f]

Overview
Comment:OFTarArchive: Add write and append support
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 0c5035dd3fad41aa70d5ac4b698c12831d263411387ac7bc3c708e1c29da188f
User & Date: js on 2017-08-05 15:22:53
Other Links: manifest | tags
Context
2017-08-05
15:50
OFTarArchiveEntry: Fix prefix handling for ustar check-in: e633b42c0b user: js tags: trunk
15:22
OFTarArchive: Add write and append support check-in: 0c5035dd3f user: js tags: trunk
12:27
Add OFMutableTarArchiveEntry check-in: 3f34a360bf user: js tags: trunk
Changes

Modified src/OFTarArchive.h from [b27de43f94] to [d9a5ab9e23].

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
#import "OFObject.h"
#import "OFTarArchiveEntry.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFStream;
@class OFTarArchive_FileReadStream;

/*!
 * @class OFTarArchive OFTarArchive.h ObjFW/OFTarArchive.h
 *
 * @brief A class for accessing and manipulating tar archives.
 */
@interface OFTarArchive: OFObject
{
	OFStream *_stream;
	enum {
		OF_TAR_ARCHIVE_MODE_READ,
		OF_TAR_ARCHIVE_MODE_WRITE,
		OF_TAR_ARCHIVE_MODE_APPEND
	} _mode;
	OFTarArchive_FileReadStream *_lastReturnedStream;
}

/*!
 * @brief Creates a new OFTarArchive object with the specified stream.
 *
 * @param stream A stream from which the tar archive will be read

 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       file.
 * @return A new, autoreleased OFTarArchive
 */
+ (instancetype)archiveWithStream: (OFStream *)stream
			     mode: (OFString *)mode;

#ifdef OF_HAVE_FILES
/*!
 * @brief Creates a new OFTarArchive object with the specified file.
 *
 * @param path The path to the tar archive
 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       file.
 * @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

 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       file.
 * @return An initialized OFTarArchive
 */
- initWithStream: (OFStream *)stream
	    mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;

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

/*!
 * @brief Returns the next entry from the tar archive or `nil` if all entries
 *	  have been read.
 *
 * This is only available in read mode.
 *
 * @warning Calling @ref nextEntry will invalidate all streams returned by
 *	    @ref streamForReadingCurrentEntry entry! Reading from an

 *	    invalidated stream will throw an @ref OFReadFailedException!

 *
 * @return The next entry from the tar archive or `nil` if all entries have
 *	   been read
 */
- (OFTarArchiveEntry *)nextEntry;

/*!
 * @brief Returns a stream for reading the current entry.
 *
 * @return A stream for reading the current entry
 */
- (OFStream *)streamForReadingCurrentEntry;



















@end

OF_ASSUME_NONNULL_END







<














|





|
>


|












|










|
>


|













|













|
>
|
>












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



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
#import "OFObject.h"
#import "OFTarArchiveEntry.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFStream;


/*!
 * @class OFTarArchive OFTarArchive.h ObjFW/OFTarArchive.h
 *
 * @brief A class for accessing and manipulating tar archives.
 */
@interface OFTarArchive: OFObject
{
	OFStream *_stream;
	enum {
		OF_TAR_ARCHIVE_MODE_READ,
		OF_TAR_ARCHIVE_MODE_WRITE,
		OF_TAR_ARCHIVE_MODE_APPEND
	} _mode;
	OF_KINDOF(OFStream *) _lastReturnedStream;
}

/*!
 * @brief Creates a new 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 a seekable stream.
 * @param mode The mode for the tar 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 OFTarArchive
 */
+ (instancetype)archiveWithStream: (OFStream *)stream
			     mode: (OFString *)mode;

#ifdef OF_HAVE_FILES
/*!
 * @brief Creates a new OFTarArchive object with the specified file.
 *
 * @param path The path to the tar archive
 * @param mode The mode for the tar 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 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 a seekable stream.
 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return An initialized OFTarArchive
 */
- initWithStream: (OFStream *)stream
	    mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;

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

/*!
 * @brief Returns the next entry from the tar archive or `nil` if all entries
 *	  have been read.
 *
 * 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 tar archive or `nil` if all entries have
 *	   been read
 */
- (OFTarArchiveEntry *)nextEntry;

/*!
 * @brief Returns a stream for reading the current entry.
 *
 * @return A stream for reading the current entry
 */
- (OFStream *)streamForReadingCurrentEntry;

/*!
 * @brief Returns a stream for writing the specified entry.
 *
 * @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!
 *
 * @param entry The entry for which a stream for writing should be returned
 * @return A stream for writing the specified entry
 */
- (OFStream *)streamForWritingEntry: (OFTarArchiveEntry *)entry;

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

OF_ASSUME_NONNULL_END

Modified src/OFTarArchive.m from [0db6ddc7de] to [69e4750cf1].

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
 * 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 "OFTarArchive.h"
#import "OFTarArchiveEntry.h"
#import "OFTarArchiveEntry+Private.h"
#import "OFStream.h"

#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"




@interface OFTarArchive_FileReadStream: OFStream
{
	OFStream *_stream;
	OFTarArchiveEntry *_entry;

	size_t _toRead;
	bool _atEndOfStream;
}

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


























@implementation OFTarArchive: OFObject
+ (instancetype)archiveWithStream: (OFStream *)stream
			     mode: (OFString *)mode
{
	return [[[self alloc] initWithStream: stream
					mode: mode] autorelease];







>
>





>







>
>
>



<

>
|







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
 * 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"

#include <inttypes.h>

#import "OFTarArchive.h"
#import "OFTarArchiveEntry.h"
#import "OFTarArchiveEntry+Private.h"
#import "OFStream.h"
#import "OFDate.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFWriteFailedException.h"

@interface OFTarArchive_FileReadStream: OFStream
{

	OFTarArchiveEntry *_entry;
	OFStream *_stream;
	uint64_t _toRead;
	bool _atEndOfStream;
}

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

@interface OFTarArchive_FileWriteStream: OFStream
{
	OFTarArchiveEntry *_entry;
	OFStream *_stream;
	uint64_t _toWrite;
}

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

static void
stringToBuffer(unsigned char *buffer, OFString *string, size_t length)
{
	size_t UTF8StringLength = [string UTF8StringLength];

	if (UTF8StringLength > length)
		@throw [OFOutOfRangeException exception];

	memcpy(buffer, [string UTF8String], UTF8StringLength);

	for (size_t i = UTF8StringLength; i < length; i++)
		buffer[i] = '\0';
}

@implementation OFTarArchive: OFObject
+ (instancetype)archiveWithStream: (OFStream *)stream
			     mode: (OFString *)mode
{
	return [[[self alloc] initWithStream: stream
					mode: mode] autorelease];
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
	self = [super init];

	@try {
		_stream = [stream retain];

		if ([mode isEqual: @"r"])
			_mode = OF_TAR_ARCHIVE_MODE_READ;




		else
			@throw [OFInvalidArgumentException exception];


























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

	return self;
}

#ifdef OF_HAVE_FILES
- initWithPath: (OFString *)path
	  mode: (OFString *)mode
{



	OFFile *file = [[OFFile alloc] initWithPath: path



					       mode: mode];

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

	return self;
}
#endif

- (void)dealloc
{
	[_stream release];
	[_lastReturnedStream release];

	[super dealloc];
}

- (OFTarArchiveEntry *)nextEntry
{
	OFTarArchiveEntry *entry;







>
>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>












>
>
>
|
>
>
>

>













|
<







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
	self = [super init];

	@try {
		_stream = [stream retain];

		if ([mode isEqual: @"r"])
			_mode = OF_TAR_ARCHIVE_MODE_READ;
		else if ([mode isEqual: @"w"])
			_mode = OF_TAR_ARCHIVE_MODE_WRITE;
		else if ([mode isEqual: @"a"])
			_mode = OF_TAR_ARCHIVE_MODE_APPEND;
		else
			@throw [OFInvalidArgumentException exception];

		if (_mode == OF_TAR_ARCHIVE_MODE_APPEND) {
			union {
				char c[1024];
				uint32_t u32[1024 / sizeof(uint32_t)];
			} buffer;
			bool empty = true;

			if (![_stream isKindOfClass: [OFSeekableStream class]])
				@throw [OFInvalidArgumentException exception];

			[(OFSeekableStream *)stream seekToOffset: -1024
							  whence: SEEK_END];
			[stream readIntoBuffer: buffer.c
				   exactLength: 1024];

			for (size_t i = 0; i < 1024 / sizeof(uint32_t); i++)
				if (buffer.u32[i] != 0)
					empty = false;

			if (!empty)
				@throw [OFInvalidFormatException exception];

			[(OFSeekableStream *)stream seekToOffset: -1024
							  whence: SEEK_END];
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

#ifdef OF_HAVE_FILES
- 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];
}

- (OFTarArchiveEntry *)nextEntry
{
	OFTarArchiveEntry *entry;
149
150
151
152
153
154
155



156
157





















































































158
159
160
161
162
163
164
		   stream: _stream];

	return entry;
}

- (OFStream *)streamForReadingCurrentEntry
{



	return [[_lastReturnedStream retain] autorelease];
}





















































































@end

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







>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
		   stream: _stream];

	return entry;
}

- (OFStream *)streamForReadingCurrentEntry
{
	if (_mode != OF_TAR_ARCHIVE_MODE_READ)
		@throw [OFInvalidArgumentException exception];

	return [[_lastReturnedStream retain] autorelease];
}

- (OFStream *)streamForWritingEntry: (OFTarArchiveEntry *)entry
{
	void *pool;
	uint64_t modificationDate;
	unsigned char buffer[512];
	uint16_t checksum = 0;

	if (_mode != OF_TAR_ARCHIVE_MODE_WRITE &&
	    _mode != OF_TAR_ARCHIVE_MODE_APPEND)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

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

	stringToBuffer(buffer, [entry fileName], 100);
	stringToBuffer(buffer + 100,
	    [OFString stringWithFormat: @"%06" PRIo32 " ", [entry mode]], 8);
	memcpy(buffer + 108, "000000 \0" "000000 \0", 16);
	stringToBuffer(buffer + 124,
	    [OFString stringWithFormat: @"%011" PRIo64 " ", [entry size]], 12);
	modificationDate = [[entry modificationDate] timeIntervalSince1970];
	stringToBuffer(buffer + 136,
	    [OFString stringWithFormat: @"%011" PRIo64 " ", modificationDate],
	    12);

	/*
	 * During checksumming, the checksum field is expected to be set to 8
	 * spaces.
	 */
	memset(buffer + 148, ' ', 8);

	buffer[156] = [entry type];
	stringToBuffer(buffer + 157, [entry targetFileName], 100);

	/* ustar */
	memcpy(buffer + 257, "ustar\0" "00", 8);
	stringToBuffer(buffer + 265, [entry owner], 32);
	stringToBuffer(buffer + 297, [entry group], 32);
	stringToBuffer(buffer + 329,
	    [OFString stringWithFormat: @"%06" PRIo32 " ", [entry deviceMajor]],
	    8);
	stringToBuffer(buffer + 337,
	    [OFString stringWithFormat: @"%06" PRIo32 " ", [entry deviceMinor]],
	    8);
	memset(buffer + 345, '\0', 155 + 12);

	/* Fill in the checksum */
	for (size_t i = 0; i < 500; i++)
		checksum += buffer[i];
	stringToBuffer(buffer + 148,
	    [OFString stringWithFormat: @"%06" PRIo16, checksum], 7);

	[_stream writeBuffer: buffer
		      length: sizeof(buffer)];

	_lastReturnedStream = [[OFTarArchive_FileWriteStream alloc]
	    initWithEntry: entry
		   stream: _stream];

	objc_autoreleasePoolPop(pool);

	return [[_lastReturnedStream retain] autorelease];
}

- (void)close
{
	[_lastReturnedStream close];
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	if (_mode == OF_TAR_ARCHIVE_MODE_WRITE ||
	    _mode == OF_TAR_ARCHIVE_MODE_APPEND) {
		char buffer[1024];
		memset(buffer, '\0', 1024);
		[_stream writeBuffer: buffer
			      length: 1024];
	}

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

@implementation OFTarArchive_FileReadStream
- initWithEntry: (OFTarArchiveEntry *)entry
	 stream: (OFStream *)stream
{
	self = [super init];
217
218
219
220
221
222
223





224
225
226
227
228
229
230
	return _atEndOfStream;
}

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






- (void)close
{
	[_stream release];
	_stream = nil;

	[super close];







>
>
>
>
>







372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
	return _atEndOfStream;
}

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

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

- (void)close
{
	[_stream release];
	_stream = nil;

	[super close];
250
251
252
253
254
255
256






















































































	size = [_entry size];

	if (size % 512 != 0)
		[_stream readIntoBuffer: buffer
			    exactLength: 512 - ((size_t)size % 512)];
}
@end





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
	size = [_entry size];

	if (size % 512 != 0)
		[_stream readIntoBuffer: buffer
			    exactLength: 512 - ((size_t)size % 512)];
}
@end

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

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

	return self;
}

- (void)dealloc
{
	[self close];

	[_entry release];

	[super dealloc];
}

- (void)lowlevelWriteBuffer: (const void *)buffer
		     length: (size_t)length
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if ((uint64_t)length > _toWrite)
		@throw [OFOutOfRangeException exception];

	@try {
		[_stream writeBuffer: buffer
			      length: length];
	} @catch (OFWriteFailedException *e) {
		_toWrite -= [e bytesWritten];
		@throw e;
	}

	_toWrite -= length;
}

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

	return (_toWrite == 0);
}

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

- (void)close
{
	uint64_t remainder = 512 - [_entry size] % 512;

	if (_toWrite > 0)
		@throw [OFTruncatedDataException exception];

	if (remainder != 512) {
		bool wasWriteBuffered = [_stream isWriteBuffered];

		[_stream setWriteBuffered: true];

		while (remainder--)
			[_stream writeInt8: 0];

		[_stream flushWriteBuffer];
		[_stream setWriteBuffered: wasWriteBuffered];
	}

	[_stream release];
	_stream = nil;

	[super close];
}
@end

Modified src/OFTarArchiveEntry.m from [9618c37951] to [35204f8874].

112
113
114
115
116
117
118


119
120
121
122
123
124
125

- initWithFileName: (OFString *)fileName
{
	self = [super init];

	@try {
		_fileName = [fileName copy];


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

	return self;
}







>
>







112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

- initWithFileName: (OFString *)fileName
{
	self = [super init];

	@try {
		_fileName = [fileName copy];
		_type = OF_TAR_ARCHIVE_ENTRY_TYPE_FILE;
		_mode = 0644;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

Modified src/OFZIPArchive.h from [86ef82b330] to [fba8310c32].

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

/*!
 * @brief Creates a new OFZIPArchive object with the specified seekable stream.
 *
 * @param stream A seekable stream from which the ZIP archive will be read
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       file.
 * @return A new, autoreleased OFZIPArchive
 */
+ (instancetype)archiveWithSeekableStream: (OFSeekableStream *)stream
				     mode: (OFString *)mode;

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

- init OF_UNAVAILABLE;

/*!
 * @brief Initializes an already allocated OFZIPArchive object with the
 *	  specified seekable stream.
 *
 * @param stream A seekable stream from which the ZIP archive will be read
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       file.
 * @return An initialized OFZIPArchive
 */
- initWithSeekableStream: (OFSeekableStream *)stream
		    mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;

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

/*!







|












|















|













|







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

/*!
 * @brief Creates a new OFZIPArchive object with the specified seekable stream.
 *
 * @param stream A seekable stream from which the ZIP archive will be read
 * @param mode The mode for the ZIP 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 OFZIPArchive
 */
+ (instancetype)archiveWithSeekableStream: (OFSeekableStream *)stream
				     mode: (OFString *)mode;

#ifdef OF_HAVE_FILES
/*!
 * @brief Creates a new OFZIPArchive object with the specified file.
 *
 * @param path The path to the ZIP file
 * @param mode The mode for the ZIP 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 OFZIPArchive
 */
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode;
#endif

- init OF_UNAVAILABLE;

/*!
 * @brief Initializes an already allocated OFZIPArchive object with the
 *	  specified seekable stream.
 *
 * @param stream A seekable stream from which the ZIP archive will be read
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return An initialized OFZIPArchive
 */
- initWithSeekableStream: (OFSeekableStream *)stream
		    mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;

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

/*!