48 |
49 | #define GM_EXPORT __attribute__((visibility("default")))
50 |
51 | /*!
52 | *
53 | * Based on "AppleSingle/AppleDouble Formats for Foreign Files Developer's Note"
54 | *
55 | * Notes:
56 | * DoubleEntryFileDatesInfo
57 | * File creation, modification, backup, and access times as number of seconds
58 | * before or after 12:00 AM Jan 1 2000 GMT as SInt32.
59 | * DoubleEntryFinderInfo
60 | * 16 bytes of FinderInfo followed by 16 bytes of extended FinderInfo.
61 | * New FinderInfo should be zero'd out. For a directory, when the Finder
62 | * encounters an entry with the init'd bit cleared, it will initialize the
63 | * frView field of the to a value indicating how the contents of the
64 | * directory should be shown. Recommend to set frView to value of 256.
65 | * DoubleEntryMacFileInfo
66 | * This is a 32 bit flag that stores locked (bit 0) and protected (bit 1).
67 | *
68 | */
69 | typedef enum {
70 | DoubleEntryInvalid = 0,
71 | DoubleEntryDataFork = 1,
72 | DoubleEntryResourceFork = 2,
73 | DoubleEntryRealName = 3,
74 | DoubleEntryComment = 4,
75 | DoubleEntryBlackAndWhiteIcon = 5,
76 | DoubleEntryColorIcon = 6,
77 | DoubleEntryFileDatesInfo = 8, // See notes
78 | DoubleEntryFinderInfo = 9, // See notes
79 | DoubleEntryMacFileInfo = 10, // See notes
80 | DoubleEntryProDosFileInfo = 11,
81 | DoubleEntryMSDosFileinfo = 12,
82 | DoubleEntryShortName = 13,
83 | DoubleEntryAFPFileInfo = 14,
84 | DoubleEntryDirectoryID = 15,
85 | } GMAppleDoubleEntryID;
86 |
87 | /*!
88 | * @class
89 | * @note This class represents a single entry in an AppleDouble file.
90 | */
91 | GM_EXPORT @interface GMAppleDoubleEntry : NSObject {
92 | @private
93 | GMAppleDoubleEntryID entryID_;
94 | NSData *data_; // Format depends on entryID_
95 | }
96 | /*!
97 | * @abstract Initializes an AppleDouble entry with ID and data.
98 | * @param entryID A valid entry identifier
99 | * @param data Raw data for the entry
100 | */
101 | - (id) initWithEntryID:(GMAppleDoubleEntryID)entryID data:(NSData *)data;
102 |
103 | /*! @abstract The entry ID */
104 | - (GMAppleDoubleEntryID) entryID;
105 |
106 | /*! @abstract The entry data */
107 | - (NSData *) data;
108 | @end
109 |
110 | /*!
111 | * @class
112 | * @note This class can be used to construct raw AppleDouble data.
113 | */
114 | GM_EXPORT @interface GMAppleDouble : NSObject {
115 | @private
116 | NSMutableArray *entries_;
117 | }
118 |
119 | /*! @abstract An autoreleased empty GMAppleDouble file */
120 | + (GMAppleDouble *) appleDouble;
121 |
122 | /*!
123 | * @abstract An autoreleased GMAppleDouble file.
124 | * @note The GMAppleDouble is pre-filled with entries from the raw
125 | * AppleDouble file data.
126 | * @param data Raw AppleDouble file data.
127 | */
128 | + (GMAppleDouble *) appleDoubleWithData:(NSData *)data;
129 |
130 | /*!
131 | * @abstract Adds an entry to the AppleDouble file.
132 | * @param entry The entry to add
133 | */
134 | - (void) addEntry:(GMAppleDoubleEntry *)entry;
135 |
136 | /*!
137 | * @abstract Adds an entry to the AppleDouble file with ID and data.
138 | * @param entryID The ID of the entry to add
139 | * @param data The raw data for the entry to add (retained)
140 | */
141 | - (void) addEntryWithID:(GMAppleDoubleEntryID)entryID data:(NSData *)data;
142 |
143 | /*!
144 | * @abstract Adds entries based on the provided raw AppleDouble file data.
145 | * @note This will attempt to parse the given data as an AppleDouble file
146 | * and add all entries found.
147 | * @param data Raw AppleDouble file data
148 | * @result YES if the provided data was parsed correctly.
149 | */
150 | - (BOOL) addEntriesFromAppleDoubleData:(NSData *)data;
151 |
152 | /*!
153 | * @abstract The set of GMAppleDoubleEntry present in this GMAppleDouble.
154 | * @result An array of GMAppleDoubleEntry.
155 | */
156 | - (NSArray *) entries;
157 |
158 | /*!
159 | * @abstract Constructs raw data for the AppleDouble file.
160 | * @result The raw data for an AppleDouble file represented by this GMAppleDouble.
161 | */
162 | - (NSData *) data;
163 |
164 | @end
165 |
166 | #undef GM_EXPORT
167 |
--------------------------------------------------------------------------------
/ZipKit/MacFUSE/GMAppleDouble.m:
--------------------------------------------------------------------------------
1 | // ================================================================
2 | // Copyright (c) 2007, Google Inc.
3 | // All rights reserved.
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 | // ================================================================
31 | //
32 | // GMAppleDouble.m
33 | // MacFUSE
34 | //
35 | // Created by ted on 12/29/07.
36 | //
37 | #import "GMAppleDouble.h"
38 | #import "libkern/OSByteOrder.h"
39 |
40 | #define GM_APPLE_DOUBLE_HEADER_MAGIC 0x00051607
41 | #define GM_APPLE_DOUBLE_HEADER_VERSION 0x00020000
42 |
43 | typedef struct {
44 | UInt32 magicNumber; // Should be 0x00051607
45 | UInt32 versionNumber; // Should be 0x00020000
46 | char filler[16]; // Zero-filled bytes.
47 | UInt16 numberOfEntries; // Number of entries.
48 | } __attribute__ ((packed)) DoubleHeader;
49 |
50 | typedef struct {
51 | UInt32 entryID; // Defines what entry is (0 is invalid)
52 | UInt32 offset; // Offset from beginning of file to entry data.
53 | UInt32 length; // Length of entry data in bytes.
54 | } __attribute__ ((packed)) DoubleEntryHeader;
55 |
56 | @implementation GMAppleDoubleEntry
57 |
58 | + (GMAppleDoubleEntry *) entryWithID :(GMAppleDoubleEntryID)entryID
59 | data :(NSData *)data {
60 | return [[GMAppleDoubleEntry alloc]
61 | initWithEntryID:entryID data:data];
62 | }
63 |
64 | - (id) initWithEntryID :(GMAppleDoubleEntryID)entryID
65 | data :(NSData *)data {
66 | if ((self = [super init])) {
67 | if (entryID == DoubleEntryInvalid || data == nil)
68 | return nil;
69 | entryID_ = entryID;
70 | data_ = data;
71 | }
72 | return self;
73 | }
74 |
75 |
76 | - (GMAppleDoubleEntryID) entryID {
77 | return entryID_;
78 | }
79 | - (NSData *) data {
80 | return data_;
81 | }
82 |
83 | @end
84 |
85 | @implementation GMAppleDouble
86 |
87 | + (GMAppleDouble *) appleDouble {
88 | return [[GMAppleDouble alloc] init];
89 | }
90 |
91 | + (GMAppleDouble *) appleDoubleWithData:(NSData *)data {
92 | GMAppleDouble *appleDouble = [[GMAppleDouble alloc] init];
93 | if ([appleDouble addEntriesFromAppleDoubleData:data])
94 | return appleDouble;
95 | return nil;
96 | }
97 |
98 | - (id) init {
99 | if ((self = [super init]))
100 | entries_ = [[NSMutableArray alloc] init];
101 | return self;
102 | }
103 |
104 |
105 | - (void) addEntry:(GMAppleDoubleEntry *)entry {
106 | [entries_ addObject:entry];
107 | }
108 |
109 | - (void) addEntryWithID:(GMAppleDoubleEntryID)entryID data:(NSData *)data {
110 | GMAppleDoubleEntry *entry = [GMAppleDoubleEntry entryWithID:entryID data:data];
111 | [self addEntry:entry];
112 | }
113 |
114 | - (BOOL) addEntriesFromAppleDoubleData:(NSData *)data {
115 | const int len = (int)[data length];
116 | DoubleHeader header;
117 | if (len < sizeof(header))
118 | return NO; // To small to even fit our header.
119 | [data getBytes:&header length:sizeof(header)];
120 | if (OSSwapBigToHostInt32(header.magicNumber) != GM_APPLE_DOUBLE_HEADER_MAGIC ||
121 | OSSwapBigToHostInt32(header.versionNumber) != GM_APPLE_DOUBLE_HEADER_VERSION)
122 | return NO; // Invalid header.
123 | int count = OSSwapBigToHostInt16(header.numberOfEntries);
124 | int offset = sizeof(DoubleHeader);
125 | if (len < (offset + (count * sizeof(DoubleEntryHeader))))
126 | return NO; // Not enough data to hold all the DoubleEntryHeader.
127 | for (int i = 0; i < count; ++i, offset += sizeof(DoubleEntryHeader)) {
128 | // Extract header
129 | DoubleEntryHeader entryHeader;
130 | NSRange range = NSMakeRange(offset, sizeof(entryHeader));
131 | [data getBytes:&entryHeader range:range];
132 |
133 | // Extract data
134 | range = NSMakeRange(OSSwapBigToHostInt32(entryHeader.offset),
135 | OSSwapBigToHostInt32(entryHeader.length));
136 | if (len < (range.location + range.length))
137 | return NO; // Given data too small to contain this entry.
138 | NSData *entryData = [data subdataWithRange:range];
139 | [self addEntryWithID:OSSwapBigToHostInt32(entryHeader.entryID) data:entryData];
140 | }
141 |
142 | return YES;
143 | }
144 |
145 | - (NSArray *) entries {
146 | return entries_;
147 | }
148 |
149 | - (NSData *) data {
150 | NSMutableData *entryListData = [NSMutableData data];
151 | NSMutableData *entryData = [NSMutableData data];
152 | int dataStartOffset =
153 | sizeof(DoubleHeader) + (int)[entries_ count] * sizeof(DoubleEntryHeader);
154 | for (int i = 0; i < [entries_ count]; ++i) {
155 | GMAppleDoubleEntry *entry = [entries_ objectAtIndex:i];
156 |
157 | DoubleEntryHeader entryHeader;
158 | memset(&entryHeader, 0, sizeof(entryHeader));
159 | entryHeader.entryID = OSSwapHostToBigInt32((UInt32)[entry entryID]);
160 | entryHeader.offset =
161 | OSSwapHostToBigInt32((UInt32)(dataStartOffset + [entryData length]));
162 | entryHeader.length = OSSwapHostToBigInt32((UInt32)[[entry data] length]);
163 | [entryListData appendBytes:&entryHeader length:sizeof(entryHeader)];
164 | [entryData appendData:[entry data]];
165 | }
166 |
167 | NSMutableData *data = [NSMutableData data];
168 |
169 | DoubleHeader header;
170 | memset(&header, 0, sizeof(header));
171 | header.magicNumber = OSSwapHostToBigConstInt32(GM_APPLE_DOUBLE_HEADER_MAGIC);
172 | header.versionNumber = OSSwapHostToBigConstInt32(GM_APPLE_DOUBLE_HEADER_VERSION);
173 | header.numberOfEntries = OSSwapHostToBigInt16((UInt16)[entries_ count]);
174 | [data appendBytes:&header length:sizeof(header)];
175 | [data appendData:entryListData];
176 | [data appendData:entryData];
177 | return data;
178 | }
179 |
180 | @end
181 |
--------------------------------------------------------------------------------
/ZipKit/NSData+ZKAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+ZKAdditions.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface NSData (ZKAdditions)
11 |
12 | - (UInt16)zk_hostInt16OffsetBy:(UInt64 *)offset;
13 | - (UInt32)zk_hostInt32OffsetBy:(UInt64 *)offset;
14 | - (UInt64)zk_hostInt64OffsetBy:(UInt64 *)offset;
15 | - (BOOL)zk_hostBoolOffsetBy:(UInt64 *)offset;
16 | - (NSString *)zk_stringOffsetBy:(UInt64 *)offset length:(NSUInteger)length;
17 | - (UInt32)zk_crc32;
18 | - (UInt32)zk_crc32:(unsigned long)crc;
19 |
20 | - (NSData *)zk_inflateWithWindowBits:(int)windowBits;
21 | - (NSData *)zk_deflateWithLevel:(int)level windowBits:(int)windowBits memoryLevel:(int)memoryLevel strategy:(int)strategy;
22 | - (NSData *)zk_inflate;
23 | - (NSData *)zk_deflate;
24 |
25 | @end
26 |
27 | @interface NSMutableData (ZKAdditions)
28 |
29 | + (NSMutableData *)zk_dataWithLittleInt16:(UInt16)value;
30 | + (NSMutableData *)zk_dataWithLittleInt32:(UInt32)value;
31 | + (NSMutableData *)zk_dataWithLittleInt64:(UInt64)value;
32 |
33 | - (void)zk_appendLittleInt16:(UInt16)value;
34 | - (void)zk_appendLittleInt32:(UInt32)value;
35 | - (void)zk_appendLittleInt64:(UInt64)value;
36 | - (void)zk_appendLittleBool:(BOOL)value;
37 | - (void)zk_appendPrecomposedUTF8String:(NSString *)value;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/ZipKit/NSData+ZKAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+ZKAdditions.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "NSData+ZKAdditions.h"
9 | #import "NSFileManager+ZKAdditions.h"
10 | #import "ZKCDHeader.h"
11 | #import "ZKCDTrailer.h"
12 | #import "ZKLFHeader.h"
13 | #import "zlib.h"
14 |
15 | @implementation NSData (ZKAdditions)
16 |
17 | - (UInt16)zk_hostInt16OffsetBy:(UInt64 *)offset {
18 | UInt16 value;
19 | NSUInteger length = sizeof(value);
20 | [self getBytes:&value range:NSMakeRange((NSUInteger) * offset, length)];
21 | *offset += length;
22 | return CFSwapInt16LittleToHost(value);
23 | }
24 |
25 | - (UInt32)zk_hostInt32OffsetBy:(UInt64 *)offset {
26 | UInt32 value;
27 | NSUInteger length = sizeof(value);
28 | [self getBytes:&value range:NSMakeRange((NSUInteger) * offset, length)];
29 | *offset += length;
30 | return CFSwapInt32LittleToHost(value);
31 | }
32 |
33 | - (UInt64)zk_hostInt64OffsetBy:(UInt64 *)offset {
34 | UInt64 value;
35 | NSUInteger length = sizeof(value);
36 | [self getBytes:&value range:NSMakeRange((NSUInteger) * offset, length)];
37 | *offset += length;
38 | return CFSwapInt64LittleToHost(value);
39 | }
40 |
41 | - (BOOL)zk_hostBoolOffsetBy:(UInt64 *)offset {
42 | UInt32 value = [self zk_hostInt32OffsetBy:offset];
43 | return value != 0;
44 | }
45 |
46 | - (NSString *)zk_stringOffsetBy:(UInt64 *)offset length:(NSUInteger)length {
47 | NSString *value = nil;
48 | NSData *subData = [self subdataWithRange:NSMakeRange((NSUInteger) * offset, length)];
49 | if (length > 0) {
50 | value = [[NSString alloc] initWithData:subData encoding:NSUTF8StringEncoding];
51 | }
52 | if (!value) {
53 | // No valid utf8 encoding, replace everything non-ascii with '?'
54 | NSMutableData *md = [subData mutableCopyWithZone:nil];
55 | unsigned char *mdd = [md mutableBytes];
56 | if ([md length] > 0) {
57 | for (unsigned int i = 0; i < [md length]; i++) {
58 | if (mdd[i] > 127) {
59 | mdd[i] = '?';
60 | }
61 | }
62 | value = [[NSString alloc] initWithData:md encoding:NSUTF8StringEncoding];
63 | }
64 | }
65 | *offset += length;
66 | return value;
67 | }
68 |
69 | - (UInt32)zk_crc32 {
70 | return [self zk_crc32:0];
71 | }
72 |
73 | - (UInt32)zk_crc32:(unsigned long)crc {
74 | return (UInt32)crc32(crc, [self bytes], (unsigned int)[self length]);
75 | }
76 |
77 | - (NSData *)zk_inflate {
78 | return [self zk_inflateWithWindowBits:(-MAX_WBITS)];
79 | }
80 |
81 | - (NSData *)zk_inflateWithWindowBits:(int)windowBits {
82 | NSUInteger full_length = [self length];
83 | NSUInteger half_length = full_length / 2;
84 |
85 | NSMutableData *inflatedData = [NSMutableData dataWithLength:full_length + half_length];
86 | BOOL done = NO;
87 | int status;
88 |
89 | z_stream strm;
90 |
91 | strm.next_in = (Bytef *)[self bytes];
92 | strm.avail_in = (unsigned int)[self length];
93 | strm.total_out = 0;
94 | strm.zalloc = Z_NULL;
95 | strm.zfree = Z_NULL;
96 |
97 | if (inflateInit2(&strm, windowBits) != Z_OK) {
98 | return nil;
99 | }
100 | while (!done) {
101 | if (strm.total_out >= [inflatedData length]) {
102 | [inflatedData increaseLengthBy:half_length];
103 | }
104 | strm.next_out = [inflatedData mutableBytes] + strm.total_out;
105 | strm.avail_out = (unsigned int)([inflatedData length] - strm.total_out);
106 | status = inflate(&strm, Z_SYNC_FLUSH);
107 | if (status == Z_STREAM_END) {
108 | done = YES;
109 | } else if (status != Z_OK) {
110 | break;
111 | }
112 | }
113 | if (inflateEnd(&strm) == Z_OK && done) {
114 | [inflatedData setLength:strm.total_out];
115 | } else {
116 | inflatedData = nil;
117 | }
118 | return inflatedData;
119 | }
120 |
121 | - (NSData *)zk_deflate {
122 | return [self zk_deflateWithLevel:Z_BEST_COMPRESSION windowBits:(-MAX_WBITS) memoryLevel:8 strategy:Z_DEFAULT_STRATEGY];
123 | }
124 |
125 | - (NSData *)zk_deflateWithLevel:(int)level windowBits:(int)windowBits memoryLevel:(int)memoryLevel strategy:(int)strategy {
126 | z_stream strm;
127 |
128 | strm.zalloc = Z_NULL;
129 | strm.zfree = Z_NULL;
130 | strm.opaque = Z_NULL;
131 | strm.total_out = 0;
132 | strm.next_in = (Bytef *)[self bytes];
133 | strm.avail_in = (unsigned int)[self length];
134 |
135 | NSMutableData *deflatedData = [NSMutableData dataWithLength:16384];
136 | if (deflateInit2(&strm, level, Z_DEFLATED, windowBits, memoryLevel, strategy) != Z_OK) {
137 | return nil;
138 | }
139 | do {
140 | if (strm.total_out >= [deflatedData length]) {
141 | [deflatedData increaseLengthBy:16384];
142 | }
143 | strm.next_out = [deflatedData mutableBytes] + strm.total_out;
144 | strm.avail_out = (unsigned int)([deflatedData length] - strm.total_out);
145 | deflate(&strm, Z_FINISH);
146 | } while (strm.avail_out == 0);
147 | deflateEnd(&strm);
148 | [deflatedData setLength:strm.total_out];
149 |
150 | return deflatedData;
151 | }
152 |
153 | @end
154 |
155 | @implementation NSMutableData (ZKAdditions)
156 |
157 | + (NSMutableData *)zk_dataWithLittleInt16:(UInt16)value {
158 | NSMutableData *data = [self data];
159 | [data zk_appendLittleInt16:value];
160 | return data;
161 | }
162 |
163 | + (NSMutableData *)zk_dataWithLittleInt32:(UInt32)value {
164 | NSMutableData *data = [self data];
165 | [data zk_appendLittleInt32:value];
166 | return data;
167 | }
168 |
169 | + (NSMutableData *)zk_dataWithLittleInt64:(UInt64)value {
170 | NSMutableData *data = [self data];
171 | [data zk_appendLittleInt64:value];
172 | return data;
173 | }
174 |
175 | - (void)zk_appendLittleInt16:(UInt16)value {
176 | UInt16 swappedValue = CFSwapInt16HostToLittle(value);
177 | [self appendBytes:&swappedValue length:sizeof(swappedValue)];
178 | }
179 |
180 | - (void)zk_appendLittleInt32:(UInt32)value {
181 | UInt32 swappedValue = CFSwapInt32HostToLittle(value);
182 | [self appendBytes:&swappedValue length:sizeof(swappedValue)];
183 | }
184 |
185 | - (void)zk_appendLittleInt64:(UInt64)value {
186 | UInt64 swappedValue = CFSwapInt64HostToLittle(value);
187 | [self appendBytes:&swappedValue length:sizeof(swappedValue)];
188 | }
189 |
190 | - (void)zk_appendLittleBool:(BOOL)value {
191 | return [self zk_appendLittleInt32:(value ? 1 : 0)];
192 | }
193 |
194 | - (void)zk_appendPrecomposedUTF8String:(NSString *)value {
195 | return [self appendData:[[value precomposedStringWithCanonicalMapping] dataUsingEncoding:NSUTF8StringEncoding]];
196 | }
197 |
198 | @end
199 |
--------------------------------------------------------------------------------
/ZipKit/NSDate+ZKAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSDate+ZKAdditions.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface NSDate (ZKAdditions)
11 |
12 | + (NSDate *)zk_dateWithDosDate:(NSUInteger)dosDate;
13 | - (UInt32)zk_dosDate;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ZipKit/NSDate+ZKAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSDate+ZKAdditions.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "NSDate+ZKAdditions.h"
9 |
10 | typedef NS_OPTIONS (NSUInteger, ZKCalendarUnit){
11 | ZKCalendarUnitEra = kCFCalendarUnitEra,
12 | ZKCalendarUnitYear = kCFCalendarUnitYear,
13 | ZKCalendarUnitMonth = kCFCalendarUnitMonth,
14 | ZKCalendarUnitDay = kCFCalendarUnitDay,
15 | ZKCalendarUnitHour = kCFCalendarUnitHour,
16 | ZKCalendarUnitMinute = kCFCalendarUnitMinute,
17 | ZKCalendarUnitSecond = kCFCalendarUnitSecond,
18 | };
19 |
20 | @implementation NSDate (ZKAdditions)
21 |
22 | + (NSDate *)zk_dateWithDosDate:(NSUInteger)dosDate {
23 | NSUInteger date = (NSUInteger)(dosDate >> 16);
24 | NSDateComponents *comps = [NSDateComponents new];
25 | comps.year = ((date & 0x0FE00) / 0x0200) + 1980;
26 | comps.month = (date & 0x1E0) / 0x20;
27 | comps.day = date & 0x1f;
28 | comps.hour = (dosDate & 0xF800) / 0x800;
29 | comps.minute = (dosDate & 0x7E0) / 0x20;
30 | comps.second = 2 * (dosDate & 0x1f);
31 | return [[NSCalendar currentCalendar] dateFromComponents:comps];
32 | }
33 |
34 | - (UInt32)zk_dosDate {
35 | NSUInteger options = ZKCalendarUnitYear | ZKCalendarUnitMonth | ZKCalendarUnitDay |
36 | ZKCalendarUnitHour | ZKCalendarUnitMinute | ZKCalendarUnitSecond;
37 | NSDateComponents *comps = [[NSCalendar currentCalendar] components:options fromDate:self];
38 | return ((UInt32)(comps.day + 32 * comps.month + 512 * (comps.year - 1980)) << 16) | (UInt32)(comps.second / 2 + 32 * comps.minute + 2048 * comps.hour);
39 | }
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/ZipKit/NSDictionary+ZKAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSDictionary+ZKAdditions.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface NSDictionary (ZKAdditions)
11 |
12 | + (NSDictionary *)zk_totalSizeAndCountDictionaryWithSize:(UInt64)size andItemCount:(UInt64)count;
13 | - (UInt64)zk_totalFileSize;
14 | - (UInt64)zk_itemCount;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ZipKit/NSDictionary+ZKAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSDictionary+ZKAdditions.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "NSDictionary+ZKAdditions.h"
9 |
10 | NSString *const ZKTotalFileSize = @"ZKTotalFileSize";
11 | NSString *const ZKItemCount = @"ZKItemCount";
12 |
13 | @implementation NSDictionary (ZKAdditions)
14 |
15 | + (NSDictionary *)zk_totalSizeAndCountDictionaryWithSize:(UInt64)size andItemCount:(UInt64)count {
16 | return @{ZKTotalFileSize: @(size), ZKItemCount: @(count)};
17 | }
18 |
19 | - (UInt64)zk_totalFileSize {
20 | return [self[ZKTotalFileSize] unsignedLongLongValue];
21 | }
22 |
23 | - (UInt64)zk_itemCount {
24 | return [self[ZKItemCount] unsignedLongLongValue];
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/ZipKit/NSFileHandle+ZKAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSFileHandle+ZKAdditions.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface NSFileHandle (ZKAdditions)
11 |
12 | + (NSFileHandle *)zk_newFileHandleForWritingAtPath:(NSString *)path;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/ZipKit/NSFileHandle+ZKAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSFileHandle+ZKAdditions.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "NSFileHandle+ZKAdditions.h"
9 |
10 | @implementation NSFileHandle (ZKAdditions)
11 |
12 | + (NSFileHandle *)zk_newFileHandleForWritingAtPath:(NSString *)path {
13 | NSFileManager *fm = [NSFileManager new];
14 | if (![fm fileExistsAtPath:path]) {
15 | [fm createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
16 | [fm createFileAtPath:path contents:nil attributes:nil];
17 | }
18 | NSFileHandle *fileHandle = [self fileHandleForWritingAtPath:path];
19 | return fileHandle;
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/ZipKit/NSFileManager+ZKAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSFileManager+ZKAdditions.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 | #import
10 |
11 | @interface NSFileManager (ZKAdditions)
12 |
13 | - (BOOL)zk_isSymLinkAtPath:(NSString *)path;
14 | - (BOOL)zk_isDirAtPath:(NSString *)path;
15 |
16 | - (UInt64)zk_dataSizeAtFilePath:(NSString *)path;
17 | - (NSDictionary *)zkTotalSizeAndItemCountAtPath:(NSString *)path usingResourceFork:(BOOL)rfFlag;
18 | #if ZK_TARGET_OS_MAC
19 | - (void)zk_combineAppleDoubleInDirectory:(NSString *)path;
20 | #endif
21 |
22 | - (NSDate *)zk_modificationDateForPath:(NSString *)path;
23 | - (UInt32)zk_posixPermissionsAtPath:(NSString *)path;
24 | - (UInt32)zk_externalFileAttributesAtPath:(NSString *)path;
25 | - (UInt32)zk_externalFileAttributesFor:(NSDictionary *)fileAttributes;
26 |
27 | - (UInt32)zk_crcForPath:(NSString *)path;
28 | - (UInt32)zk_crcForPath:(NSString *)path invoker:(id)invoker;
29 | - (UInt32)zk_crcForPath:(NSString *)path invoker:(id)invoker throttleThreadSleepTime:(NSTimeInterval)throttleThreadSleepTime;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/ZipKit/NSFileManager+ZKAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSFileManager+ZKAdditions.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "NSFileManager+ZKAdditions.h"
9 | #import "NSData+ZKAdditions.h"
10 | #import "NSDictionary+ZKAdditions.h"
11 |
12 | #if ZK_TARGET_OS_MAC
13 | #import "GMAppleDouble+ZKAdditions.h"
14 | #endif
15 |
16 | const NSUInteger ZKMaxEntriesPerFetch = 40;
17 |
18 | @implementation NSFileManager (ZKAdditions)
19 |
20 | - (BOOL)zk_isSymLinkAtPath:(NSString *)path {
21 | return [[[self attributesOfItemAtPath:path error:nil] fileType] isEqualToString:NSFileTypeSymbolicLink];
22 | }
23 |
24 | - (BOOL)zk_isDirAtPath:(NSString *)path {
25 | BOOL isDir;
26 | BOOL pathExists = [self fileExistsAtPath:path isDirectory:&isDir];
27 | return pathExists && isDir;
28 | }
29 |
30 | - (UInt64)zk_dataSizeAtFilePath:(NSString *)path {
31 | return [[self attributesOfItemAtPath:path error:nil] fileSize];
32 | }
33 |
34 | // pragmas to suppress deprecation warnings about FS*() functions deprecated in 10.8
35 |
36 | #if ZK_TARGET_OS_MAC
37 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
38 | - (void)totalsAtDirectoryFSRef:(FSRef *)fsRef usingResourceFork:(BOOL)rfFlag
39 | totalSize:(UInt64 *)size
40 | itemCount:(UInt64 *)count {
41 | FSIterator iterator;
42 | OSErr fsErr = FSOpenIterator(fsRef, kFSIterateFlat, &iterator);
43 | if (fsErr == noErr) {
44 | ItemCount actualFetched;
45 | FSRef fetchedRefs[ZKMaxEntriesPerFetch];
46 | FSCatalogInfo fetchedInfos[ZKMaxEntriesPerFetch];
47 | while (fsErr == noErr) {
48 | fsErr = FSGetCatalogInfoBulk(iterator, ZKMaxEntriesPerFetch, &actualFetched, NULL,
49 | kFSCatInfoDataSizes | kFSCatInfoRsrcSizes | kFSCatInfoNodeFlags,
50 | fetchedInfos, fetchedRefs, NULL, NULL);
51 | if ((fsErr == noErr) || (fsErr == errFSNoMoreItems)) {
52 | (*count) += actualFetched;
53 | for (ItemCount i = 0; i < actualFetched; i++) {
54 | if (fetchedInfos[i].nodeFlags & kFSNodeIsDirectoryMask) {
55 | [self totalsAtDirectoryFSRef:&fetchedRefs[i] usingResourceFork:rfFlag totalSize:size itemCount:count];
56 | } else {
57 | (*size) += fetchedInfos [i].dataLogicalSize + (rfFlag ? fetchedInfos [i].rsrcLogicalSize : 0);
58 | }
59 | }
60 | }
61 | }
62 | FSCloseIterator(iterator);
63 | }
64 | return;
65 | }
66 | #pragma GCC diagnostic warning "-Wdeprecated-declarations"
67 | #endif
68 |
69 | - (NSDictionary *)zkTotalSizeAndItemCountAtPath:(NSString *)path usingResourceFork:(BOOL)rfFlag {
70 | unsigned long long size = 0;
71 | unsigned long long count = 0;
72 | #if ZK_TARGET_OS_MAC
73 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
74 | FSRef fsRef;
75 | Boolean isDirectory;
76 | OSStatus status = FSPathMakeRef((const unsigned char *)[path fileSystemRepresentation], &fsRef, &isDirectory);
77 | if (status != noErr) {
78 | return nil;
79 | }
80 | if (isDirectory) {
81 | [self totalsAtDirectoryFSRef:&fsRef usingResourceFork:rfFlag totalSize:&size itemCount:&count];
82 | } else {
83 | count = 1;
84 | FSCatalogInfo info;
85 | OSErr fsErr = FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes | kFSCatInfoRsrcSizes, &info, NULL, NULL, NULL);
86 | if (fsErr == noErr) {
87 | size = info.dataLogicalSize + (rfFlag ? info.rsrcLogicalSize : 0);
88 | }
89 | }
90 | #pragma GCC diagnostic warning "-Wdeprecated-declarations"
91 | #else
92 | // TODO: maybe fix this for non-Mac targets
93 | size = 0;
94 | count = 0;
95 | #endif
96 | return [NSDictionary zk_totalSizeAndCountDictionaryWithSize:size andItemCount:count];
97 | }
98 |
99 | #if ZK_TARGET_OS_MAC
100 | - (void)zk_combineAppleDoubleInDirectory:(NSString *)path {
101 | if (![self zk_isDirAtPath:path]) {
102 | return;
103 | }
104 | NSArray *dirContents = [self contentsOfDirectoryAtPath:path error:nil];
105 | for (NSString *entry in dirContents) {
106 | NSString *subPath = [path stringByAppendingPathComponent:entry];
107 | if (![self zk_isSymLinkAtPath:subPath]) {
108 | if ([self zk_isDirAtPath:subPath]) {
109 | [self zk_combineAppleDoubleInDirectory:subPath];
110 | } else {
111 | // if the file is an AppleDouble file (i.e., it begins with "._") in the __MACOSX hierarchy,
112 | // find its corresponding data fork and combine them
113 | if ([subPath rangeOfString:ZKMacOSXDirectory].location != NSNotFound) {
114 | NSString *fileName = [subPath lastPathComponent];
115 | NSRange ZKDotUnderscoreRange = [fileName rangeOfString:ZKDotUnderscore];
116 | if (ZKDotUnderscoreRange.location == 0 && ZKDotUnderscoreRange.length == 2) {
117 | NSMutableArray *pathComponents =
118 | (NSMutableArray *)[[[subPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:
119 | [fileName substringFromIndex:2]] pathComponents];
120 | for (NSString *pathComponent in pathComponents) {
121 | if ([ZKMacOSXDirectory isEqualToString:pathComponent]) {
122 | [pathComponents removeObject:pathComponent];
123 | break;
124 | }
125 | }
126 | NSData *appleDoubleData = [NSData dataWithContentsOfFile:subPath];
127 | [GMAppleDouble zk_restoreAppleDoubleData:appleDoubleData toPath:[NSString pathWithComponents:pathComponents]];
128 | }
129 | }
130 | }
131 | }
132 | }
133 | }
134 | #endif
135 |
136 | - (NSDate *)zk_modificationDateForPath:(NSString *)path {
137 | return [[self attributesOfItemAtPath:path error:nil] fileModificationDate];
138 | }
139 |
140 | - (UInt32)zk_posixPermissionsAtPath:(NSString *)path {
141 | return (UInt32)[[self attributesOfItemAtPath:path error:nil] filePosixPermissions];
142 | }
143 |
144 | - (UInt32)zk_externalFileAttributesAtPath:(NSString *)path {
145 | return [self zk_externalFileAttributesFor:[self attributesOfItemAtPath:path error:nil]];
146 | }
147 |
148 | - (UInt32)zk_externalFileAttributesFor:(NSDictionary *)fileAttributes {
149 | UInt32 externalFileAttributes = 0;
150 | @try {
151 | BOOL isSymLink = [[fileAttributes fileType] isEqualToString:NSFileTypeSymbolicLink];
152 | BOOL isDir = [[fileAttributes fileType] isEqualToString:NSFileTypeDirectory];
153 | UInt32 posixPermissions = (UInt32)[fileAttributes filePosixPermissions];
154 | externalFileAttributes = posixPermissions << 16 | (isSymLink ? 0xA0004000 : (isDir ? 0x40004000 : 0x80004000));
155 | } @catch (NSException *e) {
156 | externalFileAttributes = 0;
157 | }
158 | return externalFileAttributes;
159 | }
160 |
161 | - (UInt32)zk_crcForPath:(NSString *)path {
162 | return [self zk_crcForPath:path invoker:nil throttleThreadSleepTime:0.0];
163 | }
164 |
165 | - (UInt32)zk_crcForPath:(NSString *)path invoker:(id)invoker {
166 | return [self zk_crcForPath:path invoker:invoker throttleThreadSleepTime:0.0];
167 | }
168 |
169 | - (UInt32)zk_crcForPath:(NSString *)path invoker:(id)invoker throttleThreadSleepTime:(NSTimeInterval)throttleThreadSleepTime {
170 | UInt32 crc32 = 0;
171 | path = [path stringByExpandingTildeInPath];
172 | BOOL isDirectory;
173 | if ([self fileExistsAtPath:path isDirectory:&isDirectory] && !isDirectory) {
174 | BOOL irtsIsCancelled = [invoker respondsToSelector:@selector(isCancelled)];
175 | const NSUInteger crcBlockSize = 1048576;
176 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
177 | NSData *block = [fileHandle readDataOfLength:crcBlockSize];
178 | while ([block length] > 0) {
179 | crc32 = [block zk_crc32:crc32];
180 | if (irtsIsCancelled) {
181 | if ([invoker isCancelled]) {
182 | [fileHandle closeFile];
183 | return 0;
184 | }
185 | }
186 | block = [fileHandle readDataOfLength:crcBlockSize];
187 | [NSThread sleepForTimeInterval:throttleThreadSleepTime];
188 | }
189 | [fileHandle closeFile];
190 | } else {
191 | crc32 = 0;
192 | }
193 | return crc32;
194 | }
195 |
196 | @end
197 |
--------------------------------------------------------------------------------
/ZipKit/NSString+ZKAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+ZKAdditions.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface NSString (ZKAdditions)
11 |
12 | - (UInt32)zk_precomposedUTF8Length;
13 | - (BOOL)zk_isResourceForkPath;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ZipKit/NSString+ZKAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+ZKAdditions.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "NSString+ZKAdditions.h"
9 | #import "ZKDefs.h"
10 |
11 | @implementation NSString (ZKAdditions)
12 |
13 | - (UInt32)zk_precomposedUTF8Length {
14 | return (UInt32)[[self precomposedStringWithCanonicalMapping] lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
15 | }
16 |
17 | - (BOOL)zk_isResourceForkPath {
18 | return [[self pathComponents][0] isEqualToString:ZKMacOSXDirectory];
19 | }
20 |
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/ZipKit/ZKArchive.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKArchive.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 08/05/09.
6 | //
7 |
8 | #import
9 |
10 | @class ZKCDTrailer;
11 | @class ZKArchive;
12 |
13 | @protocol ZipKitDelegate
14 | @optional
15 | - (void)onZKArchiveDidBeginZip:(ZKArchive *)archive;
16 | - (void)onZKArchiveDidBeginUnzip:(ZKArchive *)archive;
17 | - (void)onZKArchive:(ZKArchive *)archive willZipPath:(NSString *)path;
18 | - (void)onZKArchive:(ZKArchive *)archive willUnzipPath:(NSString *)path;
19 | - (void)onZKArchive:(ZKArchive *)archive didUpdateTotalSize:(UInt64)size;
20 | - (void)onZKArchive:(ZKArchive *)archive didUpdateTotalCount:(UInt64)count;
21 | - (void)onZKArchive:(ZKArchive *)archive didUpdateBytesWritten:(UInt64)byteCount;
22 | - (void)onZKArchiveDidEndZip:(ZKArchive *)archive;
23 | - (void)onZKArchiveDidEndUnzip:(ZKArchive *)archive;
24 | - (void)onZKArchiveDidCancel:(ZKArchive *)archive;
25 | - (void)onZKArchiveDidFail:(ZKArchive *)archive;
26 | - (BOOL)zkDelegateWantsSizes;
27 | @end
28 |
29 | @interface ZKArchive : NSObject {
30 | @protected
31 | // cached respondsToSelector: checks
32 | BOOL drtsDelegateWantsSizes;
33 | BOOL drtsDidBeginZip;
34 | BOOL drtsDidBeginUnzip;
35 | BOOL drtsWillZipPath;
36 | BOOL drtsWillUnzipPath;
37 | BOOL drtsDidEndZip;
38 | BOOL drtsDidEndUnzip;
39 | BOOL drtsDidCancel;
40 | BOOL drtsDidFail;
41 | BOOL drtsDidUpdateTotalSize;
42 | BOOL drtsDidUpdateTotalCount;
43 | BOOL drtsDidUpdateBytesWritten;
44 |
45 | BOOL irtsIsCancelled;
46 | }
47 |
48 | + (BOOL)validArchiveAtPath:(NSString *)path;
49 | + (NSString *)uniquify:(NSString *)path;
50 | - (void)calculateSizeAndItemCount:(NSDictionary *)userInfo;
51 | - (NSString *)uniqueExpansionDirectoryIn:(NSString *)enclosingFolder;
52 | - (void)cleanUpExpansionDirectory:(NSString *)expansionDirectory;
53 |
54 | - (BOOL)delegateWantsSizes;
55 |
56 | - (void)didBeginZip;
57 | - (void)didBeginUnzip;
58 | - (void)willZipPath:(NSString *)path;
59 | - (void)willUnzipPath:(NSString *)path;
60 | - (void)didEndZip;
61 | - (void)didEndUnzip;
62 | - (void)didCancel;
63 | - (void)didFail;
64 | - (void)didUpdateTotalSize:(NSNumber *)size;
65 | - (void)didUpdateTotalCount:(NSNumber *)count;
66 | - (void)didUpdateBytesWritten:(NSNumber *)byteCount;
67 |
68 | @property (weak, nonatomic) id invoker;
69 | @property (weak, nonatomic) id delegate;
70 | @property (copy) NSString *archivePath;
71 | @property (strong) NSMutableArray *centralDirectory;
72 | @property (strong) NSFileManager *fileManager;
73 | @property (strong) ZKCDTrailer *cdTrailer;
74 | @property (assign) NSTimeInterval throttleThreadSleepTime;
75 | @property (copy) NSString *comment;
76 |
77 | @property (assign) BOOL overwriteExistingFiles;
78 |
79 | @end
80 |
--------------------------------------------------------------------------------
/ZipKit/ZKArchive.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKArchive.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 08/05/09.
6 | //
7 |
8 | #import "ZKArchive.h"
9 | #import "NSDictionary+ZKAdditions.h"
10 | #import "NSFileManager+ZKAdditions.h"
11 | #import "ZKCDTrailer.h"
12 | #import "ZKDefs.h"
13 |
14 | #pragma mark -
15 |
16 | @implementation ZKArchive
17 |
18 | #pragma mark -
19 | #pragma mark Utility
20 |
21 | + (BOOL)validArchiveAtPath:(NSString *)path {
22 | // check that the first few bytes of the file are a local file header
23 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
24 | NSData *fileHeader = [fileHandle readDataOfLength:4];
25 | [fileHandle closeFile];
26 | UInt32 headerValue;
27 | [fileHeader getBytes:&headerValue length:sizeof(UInt32)];
28 | return CFSwapInt32LittleToHost(headerValue) == ZKLFHeaderMagicNumber;
29 | }
30 |
31 | + (NSString *)uniquify:(NSString *)path {
32 | // avoid name collisions by adding a sequence number if needed
33 | NSString *uniquePath = [NSString stringWithString:path];
34 | NSString *dir = [path stringByDeletingLastPathComponent];
35 | NSString *fileNameBase = [[path lastPathComponent] stringByDeletingPathExtension];
36 | NSString *ext = [path pathExtension];
37 | NSUInteger i = 2;
38 | NSFileManager *fm = [NSFileManager new];
39 | while ([fm fileExistsAtPath:uniquePath]) {
40 | uniquePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@ %lu", fileNameBase, (unsigned long)i++]];
41 | if (ext && [ext length] > 0) {
42 | uniquePath = [uniquePath stringByAppendingPathExtension:ext];
43 | }
44 | }
45 | return uniquePath;
46 | }
47 |
48 | - (void)calculateSizeAndItemCount:(NSDictionary *)userInfo {
49 | NSArray *paths = userInfo[ZKPathsKey];
50 | BOOL rfFlag = [userInfo[ZKusingResourceForkKey] boolValue];
51 | unsigned long long size = 0;
52 | unsigned long long count = 0;
53 | NSFileManager *fmgr = [NSFileManager new];
54 | NSDictionary *dict = nil;
55 | for (NSString *path in paths) {
56 | dict = [fmgr zkTotalSizeAndItemCountAtPath:path usingResourceFork:rfFlag];
57 | size += [dict zk_totalFileSize];
58 | count += [dict zk_itemCount];
59 | }
60 | [self performSelectorOnMainThread:@selector(didUpdateTotalSize:)
61 | withObject:@(size) waitUntilDone:NO];
62 | [self performSelectorOnMainThread:@selector(didUpdateTotalCount:)
63 | withObject:@(count) waitUntilDone:NO];
64 | }
65 |
66 | - (NSString *)uniqueExpansionDirectoryIn:(NSString *)enclosingFolder {
67 | NSString *expansionDirectory = [enclosingFolder stringByAppendingPathComponent:ZKExpansionDirectoryName];
68 | NSUInteger i = 1;
69 | while ([self.fileManager fileExistsAtPath:expansionDirectory]) {
70 | expansionDirectory = [enclosingFolder stringByAppendingPathComponent:
71 | [NSString stringWithFormat:@"%@ %lu", ZKExpansionDirectoryName, (unsigned long)i++]];
72 | }
73 | return expansionDirectory;
74 | }
75 |
76 | - (void)cleanUpExpansionDirectory:(NSString *)expansionDirectory {
77 | NSString *enclosingFolder = [expansionDirectory stringByDeletingLastPathComponent];
78 | NSArray *dirContents = [self.fileManager contentsOfDirectoryAtPath:expansionDirectory error:nil];
79 | for (NSString *item in dirContents) {
80 | if (![item isEqualToString:ZKMacOSXDirectory]) {
81 | NSString *subPath = [expansionDirectory stringByAppendingPathComponent:item];
82 | NSString *dest = [enclosingFolder stringByAppendingPathComponent:item];
83 |
84 | if (self.overwriteExistingFiles) {
85 |
86 | if ([self.fileManager fileExistsAtPath:dest]) {
87 | [self.fileManager removeItemAtPath:dest error:nil];
88 | }
89 |
90 | } else {
91 |
92 | NSUInteger i = 2;
93 | while ([self.fileManager fileExistsAtPath:dest]) {
94 | NSString *ext = [item pathExtension];
95 | dest = [enclosingFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@ %lu",
96 | [item stringByDeletingPathExtension], (unsigned long)i++]];
97 | if (ext && [ext length] > 0) {
98 | dest = [dest stringByAppendingPathExtension:ext];
99 | }
100 | }
101 |
102 | }
103 |
104 | [self.fileManager moveItemAtPath:subPath toPath:dest error:nil];
105 | }
106 | }
107 | [self.fileManager removeItemAtPath:expansionDirectory error:nil];
108 |
109 | }
110 |
111 | #pragma mark -
112 | #pragma mark Accessors
113 |
114 | - (NSString *)comment {
115 | return self.cdTrailer.comment;
116 | }
117 | - (void)setComment:(NSString *)comment {
118 | self.cdTrailer.comment = comment;
119 | }
120 |
121 | #pragma mark -
122 | #pragma mark Delegate
123 |
124 | - (void)setInvoker:(id)i {
125 | _invoker = i;
126 | if (_invoker) {
127 | irtsIsCancelled = [self.invoker respondsToSelector:@selector(isCancelled)];
128 | } else {
129 | irtsIsCancelled = NO;
130 | }
131 | }
132 |
133 | - (void)setDelegate:(id)d {
134 | _delegate = d;
135 | if (_delegate) {
136 | drtsDelegateWantsSizes = [_delegate respondsToSelector:@selector(zkDelegateWantsSizes)];
137 | drtsDidBeginZip = [_delegate respondsToSelector:@selector(onZKArchiveDidBeginZip:)];
138 | drtsDidBeginUnzip = [_delegate respondsToSelector:@selector(onZKArchiveDidBeginUnzip:)];
139 | drtsWillZipPath = [_delegate respondsToSelector:@selector(onZKArchive:willZipPath:)];
140 | drtsWillUnzipPath = [_delegate respondsToSelector:@selector(onZKArchive:willUnzipPath:)];
141 | drtsDidEndZip = [_delegate respondsToSelector:@selector(onZKArchiveDidEndZip:)];
142 | drtsDidEndUnzip = [_delegate respondsToSelector:@selector(onZKArchiveDidEndUnzip:)];
143 | drtsDidCancel = [_delegate respondsToSelector:@selector(onZKArchiveDidCancel:)];
144 | drtsDidFail = [_delegate respondsToSelector:@selector(onZKArchiveDidFail:)];
145 | drtsDidUpdateTotalSize = [_delegate respondsToSelector:@selector(onZKArchive:didUpdateTotalSize:)];
146 | drtsDidUpdateTotalCount = [_delegate respondsToSelector:@selector(onZKArchive:didUpdateTotalCount:)];
147 | drtsDidUpdateBytesWritten = [_delegate respondsToSelector:@selector(onZKArchive:didUpdateBytesWritten:)];
148 | } else {
149 | drtsDelegateWantsSizes = NO;
150 | drtsDidBeginZip = NO;
151 | drtsDidBeginUnzip = NO;
152 | drtsWillZipPath = NO;
153 | drtsWillUnzipPath = NO;
154 | drtsDidEndZip = NO;
155 | drtsDidEndUnzip = NO;
156 | drtsDidCancel = NO;
157 | drtsDidFail = NO;
158 | drtsDidUpdateTotalSize = NO;
159 | drtsDidUpdateTotalCount = NO;
160 | drtsDidUpdateBytesWritten = NO;
161 | }
162 | }
163 |
164 | - (BOOL)delegateWantsSizes {
165 | BOOL delegateWantsSizes = NO;
166 | if (drtsDelegateWantsSizes) {
167 | delegateWantsSizes = [self.delegate zkDelegateWantsSizes];
168 | }
169 | return delegateWantsSizes;
170 | }
171 |
172 | - (void)didBeginZip {
173 | if (drtsDidBeginZip) {
174 | [self.delegate onZKArchiveDidBeginZip:self];
175 | }
176 | }
177 |
178 | - (void)didBeginUnzip {
179 | if (drtsDidBeginUnzip) {
180 | [self.delegate onZKArchiveDidBeginUnzip:self];
181 | }
182 | }
183 |
184 | - (void)willZipPath:(NSString *)path {
185 | if (drtsWillZipPath) {
186 | [self.delegate onZKArchive:self willZipPath:path];
187 | }
188 | }
189 |
190 | - (void)willUnzipPath:(NSString *)path {
191 | if (drtsWillUnzipPath) {
192 | [self.delegate onZKArchive:self willUnzipPath:path];
193 | }
194 | }
195 |
196 | - (void)didEndZip {
197 | if (drtsDidEndZip) {
198 | [self.delegate onZKArchiveDidEndZip:self];
199 | }
200 | }
201 |
202 | - (void)didEndUnzip {
203 | if (drtsDidEndUnzip) {
204 | [self.delegate onZKArchiveDidEndUnzip:self];
205 | }
206 | }
207 |
208 | - (void)didCancel {
209 | if (drtsDidCancel) {
210 | [self.delegate onZKArchiveDidCancel:self];
211 | }
212 | }
213 |
214 | - (void)didFail {
215 | if (drtsDidFail) {
216 | [self.delegate onZKArchiveDidFail:self];
217 | }
218 | }
219 |
220 | - (void)didUpdateTotalSize:(NSNumber *)size {
221 | if (drtsDidUpdateTotalSize) {
222 | [self.delegate onZKArchive:self didUpdateTotalSize:[size unsignedLongLongValue]];
223 | }
224 | }
225 |
226 | - (void)didUpdateTotalCount:(NSNumber *)count {
227 | if (drtsDidUpdateTotalCount) {
228 | [self.delegate onZKArchive:self didUpdateTotalCount:[count unsignedLongLongValue]];
229 | }
230 | }
231 |
232 | - (void)didUpdateBytesWritten:(NSNumber *)byteCount {
233 | if (drtsDidUpdateBytesWritten) {
234 | [self.delegate onZKArchive:self didUpdateBytesWritten:[byteCount unsignedLongLongValue]];
235 | }
236 | }
237 |
238 | #pragma mark -
239 | #pragma mark Setup
240 |
241 | - (id)init {
242 | if (self = [super init]) {
243 | self.invoker = nil;
244 | self.delegate = nil;
245 | self.archivePath = nil;
246 | self.centralDirectory = [NSMutableArray array];
247 | self.fileManager = [NSFileManager new];
248 | self.cdTrailer = [ZKCDTrailer new];
249 | self.throttleThreadSleepTime = 0.0;
250 | }
251 | return self;
252 | }
253 |
254 | - (void)dealloc {
255 | self.invoker = nil;
256 | self.delegate = nil;
257 | }
258 |
259 | - (NSString *)description {
260 | return [NSString stringWithFormat:@"%@\n\ttrailer:%@\n\tcentral directory:%@", self.archivePath, self.cdTrailer, self.centralDirectory];
261 | }
262 |
263 | @dynamic comment;
264 |
265 | @end
266 |
--------------------------------------------------------------------------------
/ZipKit/ZKCDHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKCDHeader.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface ZKCDHeader : NSObject
11 |
12 | + (ZKCDHeader *)recordWithData:(NSData *)data atOffset:(UInt64)offset;
13 | + (ZKCDHeader *)recordWithArchivePath:(NSString *)path atOffset:(UInt64)offset;
14 | - (void)parseZip64ExtraField;
15 | - (NSData *)zip64ExtraField;
16 | - (NSData *)data;
17 | - (NSUInteger)length;
18 | - (BOOL)useZip64Extensions;
19 | - (NSNumber *)posixPermissions;
20 | - (BOOL)isDirectory;
21 | - (BOOL)isSymLink;
22 | - (BOOL)isResourceFork;
23 |
24 | @property (assign) UInt32 magicNumber;
25 | @property (assign) UInt32 versionMadeBy;
26 | @property (assign) UInt32 versionNeededToExtract;
27 | @property (assign) UInt32 generalPurposeBitFlag;
28 | @property (assign) UInt32 compressionMethod;
29 | @property (strong) NSDate *lastModDate;
30 | @property (assign) UInt32 crc;
31 | @property (assign) UInt64 compressedSize;
32 | @property (assign) UInt64 uncompressedSize;
33 | @property (assign) UInt32 filenameLength;
34 | @property (assign) UInt32 extraFieldLength;
35 | @property (assign) UInt32 commentLength;
36 | @property (assign) UInt32 diskNumberStart;
37 | @property (assign) UInt32 internalFileAttributes;
38 | @property (assign) UInt32 externalFileAttributes;
39 | @property (assign) UInt64 localHeaderOffset;
40 | @property (copy) NSString *filename;
41 | @property (strong) NSData *extraField;
42 | @property (copy) NSString *comment;
43 | @property (strong) NSMutableData *cachedData;
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/ZipKit/ZKCDHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKCDHeader.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "ZKCDHeader.h"
9 | #import "NSDate+ZKAdditions.h"
10 | #import "NSData+ZKAdditions.h"
11 | #import "NSString+ZKAdditions.h"
12 | #import "ZKDefs.h"
13 | #import "zlib.h"
14 |
15 | @implementation ZKCDHeader
16 |
17 | - (id)init {
18 | if (self = [super init]) {
19 | self.magicNumber = ZKCDHeaderMagicNumber;
20 | self.versionNeededToExtract = 20;
21 | self.versionMadeBy = 789;
22 | self.generalPurposeBitFlag = 0;
23 | self.compressionMethod = Z_DEFLATED;
24 | self.lastModDate = [NSDate date];
25 | self.crc = 0;
26 | self.compressedSize = 0;
27 | self.uncompressedSize = 0;
28 | self.filenameLength = 0;
29 | self.extraFieldLength = 0;
30 | self.commentLength = 0;
31 | self.diskNumberStart = 0;
32 | self.internalFileAttributes = 0;
33 | self.externalFileAttributes = 0;
34 | self.localHeaderOffset = 0;
35 | self.extraField = nil;
36 | self.comment = nil;
37 |
38 | [self addObserver:self forKeyPath:@"compressedSize" options:NSKeyValueObservingOptionNew context:nil];
39 | [self addObserver:self forKeyPath:@"uncompressedSize" options:NSKeyValueObservingOptionNew context:nil];
40 | [self addObserver:self forKeyPath:@"localHeaderOffset" options:NSKeyValueObservingOptionNew context:nil];
41 | [self addObserver:self forKeyPath:@"extraField" options:NSKeyValueObservingOptionNew context:nil];
42 | [self addObserver:self forKeyPath:@"filename" options:NSKeyValueObservingOptionNew context:nil];
43 | [self addObserver:self forKeyPath:@"comment" options:NSKeyValueObservingOptionNew context:nil];
44 | }
45 | return self;
46 | }
47 |
48 | - (void)removeObservers {
49 | [self removeObserver:self forKeyPath:@"compressedSize"];
50 | [self removeObserver:self forKeyPath:@"uncompressedSize"];
51 | [self removeObserver:self forKeyPath:@"localHeaderOffset"];
52 | [self removeObserver:self forKeyPath:@"extraField"];
53 | [self removeObserver:self forKeyPath:@"filename"];
54 | [self removeObserver:self forKeyPath:@"comment"];
55 | }
56 |
57 | - (void)dealloc {
58 | [self removeObservers];
59 | }
60 |
61 |
62 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
63 | if ([keyPath isEqualToString:@"compressedSize"]
64 | || [keyPath isEqualToString:@"uncompressedSize"]
65 | || [keyPath isEqualToString:@"localHeaderOffset"]) {
66 | self.versionNeededToExtract = ([self useZip64Extensions] ? 45 : 20);
67 | } else if ([keyPath isEqualToString:@"extraField"] && self.extraFieldLength < 1) {
68 | self.extraFieldLength = (UInt32)[self.extraField length];
69 | } else if ([keyPath isEqualToString:@"filename"] && self.filenameLength < 1) {
70 | self.filenameLength = [self.filename zk_precomposedUTF8Length];
71 | } else if ([keyPath isEqualToString:@"comment"] && self.commentLength < 1) {
72 | self.commentLength = [self.comment zk_precomposedUTF8Length];
73 | }
74 | }
75 |
76 | + (ZKCDHeader *)recordWithData:(NSData *)data atOffset:(UInt64)offset {
77 | if (!data || data.length < 1) {
78 | return nil;
79 | }
80 | UInt32 mn = [data zk_hostInt32OffsetBy:&offset];
81 | if (mn != ZKCDHeaderMagicNumber) {
82 | return nil;
83 | }
84 | ZKCDHeader *record = [ZKCDHeader new];
85 | record.magicNumber = mn;
86 | record.versionMadeBy = [data zk_hostInt16OffsetBy:&offset];
87 | record.versionNeededToExtract = [data zk_hostInt16OffsetBy:&offset];
88 | record.generalPurposeBitFlag = [data zk_hostInt16OffsetBy:&offset];
89 | record.compressionMethod = [data zk_hostInt16OffsetBy:&offset];
90 | record.lastModDate = [NSDate zk_dateWithDosDate:[data zk_hostInt32OffsetBy:&offset]];
91 | record.crc = [data zk_hostInt32OffsetBy:&offset];
92 | record.compressedSize = [data zk_hostInt32OffsetBy:&offset];
93 | record.uncompressedSize = [data zk_hostInt32OffsetBy:&offset];
94 | record.filenameLength = [data zk_hostInt16OffsetBy:&offset];
95 | record.extraFieldLength = [data zk_hostInt16OffsetBy:&offset];
96 | record.commentLength = [data zk_hostInt16OffsetBy:&offset];
97 | record.diskNumberStart = [data zk_hostInt16OffsetBy:&offset];
98 | record.internalFileAttributes = [data zk_hostInt16OffsetBy:&offset];
99 | record.externalFileAttributes = [data zk_hostInt32OffsetBy:&offset];
100 | record.localHeaderOffset = [data zk_hostInt32OffsetBy:&offset];
101 | if ([data length] > ZKCDHeaderFixedDataLength) {
102 | if (record.filenameLength) {
103 | record.filename = [data zk_stringOffsetBy:&offset length:record.filenameLength];
104 | }
105 | if (record.extraFieldLength) {
106 | record.extraField = [data subdataWithRange:NSMakeRange((NSUInteger)offset, record.extraFieldLength)];
107 | offset += record.extraFieldLength;
108 | [record parseZip64ExtraField];
109 | }
110 | if (record.commentLength) {
111 | record.comment = [data zk_stringOffsetBy:&offset length:record.commentLength];
112 | }
113 | }
114 | return record;
115 | }
116 |
117 | + (ZKCDHeader *)recordWithArchivePath:(NSString *)path atOffset:(UInt64)offset {
118 | NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
119 | [file seekToFileOffset:offset];
120 | NSData *fixedData = [file readDataOfLength:ZKCDHeaderFixedDataLength];
121 | ZKCDHeader *record = [self recordWithData:fixedData atOffset:0];
122 | if (record.filenameLength > 0) {
123 | NSData *data = [file readDataOfLength:record.filenameLength];
124 | record.filename = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
125 | }
126 | if (record.extraFieldLength > 0) {
127 | record.extraField = [file readDataOfLength:record.extraFieldLength];
128 | [record parseZip64ExtraField];
129 | }
130 | if (record.commentLength > 0) {
131 | NSData *data = [file readDataOfLength:record.commentLength];
132 | record.comment = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
133 | } else {
134 | record.comment = nil;
135 | }
136 |
137 | [file closeFile];
138 | return record;
139 | }
140 |
141 | - (NSData *)data {
142 | if (!self.cachedData || ([self.cachedData length] < ZKCDHeaderFixedDataLength)) {
143 | self.extraField = [self zip64ExtraField];
144 |
145 | self.cachedData = [NSMutableData zk_dataWithLittleInt32:self.magicNumber];
146 | [self.cachedData zk_appendLittleInt16:self.versionMadeBy];
147 | [self.cachedData zk_appendLittleInt16:self.versionNeededToExtract];
148 | [self.cachedData zk_appendLittleInt16:self.generalPurposeBitFlag];
149 | [self.cachedData zk_appendLittleInt16:self.compressionMethod];
150 | [self.cachedData zk_appendLittleInt32:[self.lastModDate zk_dosDate]];
151 | [self.cachedData zk_appendLittleInt32:self.crc];
152 | if ([self useZip64Extensions]) {
153 | [self.cachedData zk_appendLittleInt32:0xFFFFFFFF];
154 | [self.cachedData zk_appendLittleInt32:0xFFFFFFFF];
155 | } else {
156 | [self.cachedData zk_appendLittleInt32:(UInt32)self.compressedSize];
157 | [self.cachedData zk_appendLittleInt32:(UInt32)self.uncompressedSize];
158 | }
159 | [self.cachedData zk_appendLittleInt16:[self.filename zk_precomposedUTF8Length]];
160 | [self.cachedData zk_appendLittleInt16:[self.extraField length]];
161 | [self.cachedData zk_appendLittleInt16:[self.comment zk_precomposedUTF8Length]];
162 | [self.cachedData zk_appendLittleInt16:self.diskNumberStart];
163 | [self.cachedData zk_appendLittleInt16:self.internalFileAttributes];
164 | [self.cachedData zk_appendLittleInt32:self.externalFileAttributes];
165 | if ([self useZip64Extensions]) {
166 | [self.cachedData zk_appendLittleInt32:0xFFFFFFFF];
167 | } else {
168 | [self.cachedData zk_appendLittleInt32:(UInt32)self.localHeaderOffset];
169 | }
170 | [self.cachedData zk_appendPrecomposedUTF8String:self.filename];
171 | [self.cachedData appendData:self.extraField];
172 | [self.cachedData zk_appendPrecomposedUTF8String:self.comment];
173 | }
174 | return self.cachedData;
175 | }
176 |
177 | - (void)parseZip64ExtraField {
178 | NSUInteger tag, length;
179 | UInt64 offset = 0;
180 | while (offset < self.extraFieldLength) {
181 | tag = [self.extraField zk_hostInt16OffsetBy:&offset];
182 | length = [self.extraField zk_hostInt16OffsetBy:&offset];
183 | if (tag == 0x0001) {
184 | if (length >= 8) {
185 | self.uncompressedSize = [self.extraField zk_hostInt64OffsetBy:&offset];
186 | }
187 | if (length >= 16) {
188 | self.compressedSize = [self.extraField zk_hostInt64OffsetBy:&offset];
189 | }
190 | if (length >= 24) {
191 | self.localHeaderOffset = [self.extraField zk_hostInt64OffsetBy:&offset];
192 | }
193 | break;
194 | } else {
195 | offset += length;
196 | }
197 | }
198 | }
199 |
200 | - (NSData *)zip64ExtraField {
201 | NSMutableData *zip64ExtraField = nil;
202 | if ([self useZip64Extensions]) {
203 | zip64ExtraField = [NSMutableData zk_dataWithLittleInt16:0x0001];
204 | [zip64ExtraField zk_appendLittleInt16:24];
205 | [zip64ExtraField zk_appendLittleInt64:self.uncompressedSize];
206 | [zip64ExtraField zk_appendLittleInt64:self.compressedSize];
207 | [zip64ExtraField zk_appendLittleInt64:self.localHeaderOffset];
208 | }
209 | return zip64ExtraField;
210 | }
211 |
212 | - (NSUInteger)length {
213 | if (!self.extraField || [self.extraField length] == 0) {
214 | self.extraField = [self zip64ExtraField];
215 | }
216 | return ZKCDHeaderFixedDataLength + self.filenameLength + self.commentLength + [self.extraField length];
217 | }
218 |
219 | - (BOOL)useZip64Extensions {
220 | return (self.uncompressedSize >= 0xFFFFFFFF) || (self.compressedSize >= 0xFFFFFFFF) || (self.localHeaderOffset >= 0xFFFFFFFF);
221 | }
222 |
223 | - (NSString *)description {
224 | return [NSString stringWithFormat:@"%@ modified %@, %qu bytes (%qu compressed), @ %qu",
225 | self.filename, self.lastModDate, self.uncompressedSize, self.compressedSize, self.localHeaderOffset];
226 | }
227 |
228 | - (NSNumber *)posixPermissions {
229 | // if posixPermissions are 0, e.g. on Windows-produced archives, then default them to rwxr-wr-w a la Archive Utility
230 | return @((self.externalFileAttributes >> 16 & 0x1FF) ? : 0755U);
231 | }
232 |
233 | - (BOOL)isDirectory {
234 | uLong type = self.externalFileAttributes >> 29 & 0x1F;
235 | if (0 == (self.versionMadeBy >> 8)) { // DOS-originated archive
236 | type = self.externalFileAttributes >> 4;
237 | return (type == 0x01) && ![self isSymLink];
238 | }
239 | return (0x02 == type) && ![self isSymLink];
240 | }
241 |
242 | - (BOOL)isSymLink {
243 | uLong type = self.externalFileAttributes >> 29 & 0x1F;
244 | return 0x05 == type;
245 | }
246 |
247 | - (BOOL)isResourceFork {
248 | return [self.filename zk_isResourceForkPath];
249 | }
250 |
251 | @end
252 |
--------------------------------------------------------------------------------
/ZipKit/ZKCDTrailer.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKCDTrailer.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface ZKCDTrailer : NSObject
11 |
12 | + (ZKCDTrailer *)recordWithData:(NSData *)data atOffset:(UInt64)offset;
13 | + (ZKCDTrailer *)recordWithData:(NSData *)data;
14 | + (ZKCDTrailer *)recordWithArchivePath:(NSString *)path;
15 | - (NSData *)data;
16 | - (NSUInteger)length;
17 | - (BOOL)useZip64Extensions;
18 |
19 | @property (assign) UInt32 magicNumber;
20 | @property (assign) UInt32 thisDiskNumber;
21 | @property (assign) UInt32 diskNumberWithStartOfCentralDirectory;
22 | @property (assign) UInt32 numberOfCentralDirectoryEntriesOnThisDisk;
23 | @property (assign) UInt32 totalNumberOfCentralDirectoryEntries;
24 | @property (assign) UInt64 sizeOfCentralDirectory;
25 | @property (assign) UInt64 offsetOfStartOfCentralDirectory;
26 | @property (assign) UInt32 commentLength;
27 | @property (copy) NSString *comment;
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/ZipKit/ZKCDTrailer.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKCDTrailer.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "ZKCDTrailer.h"
9 | #import "NSData+ZKAdditions.h"
10 | #import "NSString+ZKAdditions.h"
11 | #import "ZKDefs.h"
12 | #import "zlib.h"
13 |
14 | @implementation ZKCDTrailer
15 |
16 | - (id)init {
17 | if (self = [super init]) {
18 | [self addObserver:self forKeyPath:@"comment" options:NSKeyValueObservingOptionNew context:nil];
19 |
20 | self.magicNumber = ZKCDTrailerMagicNumber;
21 | self.thisDiskNumber = 0;
22 | self.diskNumberWithStartOfCentralDirectory = 0;
23 | self.numberOfCentralDirectoryEntriesOnThisDisk = 0;
24 | self.totalNumberOfCentralDirectoryEntries = 0;
25 | self.sizeOfCentralDirectory = 0;
26 | self.offsetOfStartOfCentralDirectory = 0;
27 | self.comment = @"Archive created with ZipKit";
28 | }
29 | return self;
30 | }
31 |
32 | - (void)removeObservers {
33 | [self removeObserver:self forKeyPath:@"comment"];
34 | }
35 |
36 |
37 | - (void)dealloc {
38 | [self removeObservers];
39 | }
40 |
41 |
42 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
43 | if ([keyPath isEqualToString:@"comment"] && self.commentLength < 1) {
44 | self.commentLength = [self.comment zk_precomposedUTF8Length];
45 | }
46 | }
47 |
48 | + (ZKCDTrailer *)recordWithData:(NSData *)data atOffset:(UInt64)offset {
49 | if (!data || data.length < 1) {
50 | return nil;
51 | }
52 | UInt32 mn = [data zk_hostInt32OffsetBy:&offset];
53 | if (mn != ZKCDTrailerMagicNumber) {
54 | return nil;
55 | }
56 | ZKCDTrailer *record = [ZKCDTrailer new];
57 | record.magicNumber = mn;
58 | record.thisDiskNumber = [data zk_hostInt16OffsetBy:&offset];
59 | record.diskNumberWithStartOfCentralDirectory = [data zk_hostInt16OffsetBy:&offset];
60 | record.numberOfCentralDirectoryEntriesOnThisDisk = [data zk_hostInt16OffsetBy:&offset];
61 | record.totalNumberOfCentralDirectoryEntries = [data zk_hostInt16OffsetBy:&offset];
62 | record.sizeOfCentralDirectory = [data zk_hostInt32OffsetBy:&offset];
63 | record.offsetOfStartOfCentralDirectory = [data zk_hostInt32OffsetBy:&offset];
64 | record.commentLength = [data zk_hostInt16OffsetBy:&offset];
65 | if (record.commentLength > 0) {
66 | record.comment = [data zk_stringOffsetBy:&offset length:record.commentLength];
67 | } else {
68 | record.comment = nil;
69 | }
70 | return record;
71 | }
72 |
73 | + (ZKCDTrailer *)recordWithData:(NSData *)data {
74 | UInt32 trailerCheck = 0;
75 | NSInteger offset = [data length] - sizeof(trailerCheck);
76 | while (trailerCheck != ZKCDTrailerMagicNumber && offset > 0) {
77 | UInt64 o = offset;
78 | trailerCheck = [data zk_hostInt32OffsetBy:&o];
79 | offset--;
80 | }
81 | if (offset < 1) {
82 | return nil;
83 | }
84 | ZKCDTrailer *record = [self recordWithData:data atOffset:++offset];
85 | return record;
86 | }
87 |
88 | + (ZKCDTrailer *)recordWithArchivePath:(NSString *)path {
89 | NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
90 | unsigned long long fileOffset = [file seekToEndOfFile];
91 | for (UInt32 trailerCheck = 0; trailerCheck != ZKCDTrailerMagicNumber && fileOffset > 0; fileOffset--) {
92 | [file seekToFileOffset:fileOffset];
93 | NSData *data = [file readDataOfLength:sizeof(UInt32)];
94 | [data getBytes:&trailerCheck length:sizeof(UInt32)];
95 | }
96 | if (fileOffset < 1) {
97 | [file closeFile];
98 | return nil;
99 | }
100 | fileOffset++;
101 | [file seekToFileOffset:fileOffset];
102 | NSData *data = [file readDataToEndOfFile];
103 | [file closeFile];
104 | ZKCDTrailer *record = [self recordWithData:data atOffset:(NSUInteger)0];
105 | return record;
106 | }
107 |
108 | - (NSData *)data {
109 | NSMutableData *data = [NSMutableData zk_dataWithLittleInt32:self.magicNumber];
110 | [data zk_appendLittleInt16:self.thisDiskNumber];
111 | [data zk_appendLittleInt16:self.diskNumberWithStartOfCentralDirectory];
112 | [data zk_appendLittleInt16:self.numberOfCentralDirectoryEntriesOnThisDisk];
113 | [data zk_appendLittleInt16:self.totalNumberOfCentralDirectoryEntries];
114 | if ([self useZip64Extensions]) {
115 | [data zk_appendLittleInt32:0xFFFFFFFF];
116 | [data zk_appendLittleInt32:0xFFFFFFFF];
117 | } else {
118 | [data zk_appendLittleInt32:(UInt32)self.sizeOfCentralDirectory];
119 | [data zk_appendLittleInt32:(UInt32)self.offsetOfStartOfCentralDirectory];
120 | }
121 | [data zk_appendLittleInt16:[self.comment zk_precomposedUTF8Length]];
122 | [data zk_appendPrecomposedUTF8String:self.comment];
123 | return data;
124 | }
125 |
126 | - (NSUInteger)length {
127 | return ZKCDTrailerFixedDataLength + [self.comment length];
128 | }
129 |
130 | - (BOOL)useZip64Extensions {
131 | return (self.sizeOfCentralDirectory >= 0xFFFFFFFF) || (self.offsetOfStartOfCentralDirectory >= 0xFFFFFFFF);
132 | }
133 |
134 | - (NSString *)description {
135 | return [NSString stringWithFormat:@"%u entries (%qu bytes) @: %qu",
136 | (unsigned int)self.totalNumberOfCentralDirectoryEntries,
137 | self.sizeOfCentralDirectory,
138 | self.offsetOfStartOfCentralDirectory];
139 | }
140 |
141 | @end
142 |
--------------------------------------------------------------------------------
/ZipKit/ZKCDTrailer64.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKCDTrailer64.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface ZKCDTrailer64 : NSObject
11 |
12 | + (ZKCDTrailer64 *)recordWithData:(NSData *)data atOffset:(UInt64)offset;
13 | + (ZKCDTrailer64 *)recordWithArchivePath:(NSString *)path atOffset:(UInt64)offset;
14 |
15 | - (NSData *)data;
16 | - (NSUInteger)length;
17 |
18 | @property (assign) UInt32 magicNumber;
19 | @property (assign) unsigned long long sizeOfTrailer;
20 | @property (assign) UInt32 versionMadeBy;
21 | @property (assign) UInt32 versionNeededToExtract;
22 | @property (assign) UInt32 thisDiskNumber;
23 | @property (assign) UInt32 diskNumberWithStartOfCentralDirectory;
24 | @property (assign) UInt64 numberOfCentralDirectoryEntriesOnThisDisk;
25 | @property (assign) UInt64 totalNumberOfCentralDirectoryEntries;
26 | @property (assign) UInt64 sizeOfCentralDirectory;
27 | @property (assign) UInt64 offsetOfStartOfCentralDirectory;
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/ZipKit/ZKCDTrailer64.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKCDTrailer64.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "ZKCDTrailer64.h"
9 | #import "NSData+ZKAdditions.h"
10 | #import "ZKDefs.h"
11 |
12 | @implementation ZKCDTrailer64
13 |
14 | - (id)init {
15 | if (self = [super init]) {
16 | self.magicNumber = ZKCDTrailer64MagicNumber;
17 | self.sizeOfTrailer = 44;
18 | self.versionMadeBy = 789;
19 | self.versionNeededToExtract = 45;
20 | self.thisDiskNumber = 0;
21 | self.diskNumberWithStartOfCentralDirectory = 0;
22 | }
23 | return self;
24 | }
25 |
26 | + (ZKCDTrailer64 *)recordWithData:(NSData *)data atOffset:(UInt64)offset {
27 | if (!data || data.length < 1) {
28 | return nil;
29 | }
30 | UInt32 mn = [data zk_hostInt32OffsetBy:&offset];
31 | if (mn != ZKCDTrailer64MagicNumber) {
32 | return nil;
33 | }
34 | ZKCDTrailer64 *record = [ZKCDTrailer64 new];
35 | record.magicNumber = mn;
36 | record.sizeOfTrailer = [data zk_hostInt64OffsetBy:&offset];
37 | record.versionMadeBy = [data zk_hostInt16OffsetBy:&offset];
38 | record.versionNeededToExtract = [data zk_hostInt16OffsetBy:&offset];
39 | record.thisDiskNumber = [data zk_hostInt32OffsetBy:&offset];
40 | record.diskNumberWithStartOfCentralDirectory = [data zk_hostInt32OffsetBy:&offset];
41 | record.numberOfCentralDirectoryEntriesOnThisDisk = [data zk_hostInt64OffsetBy:&offset];
42 | record.totalNumberOfCentralDirectoryEntries = [data zk_hostInt64OffsetBy:&offset];
43 | record.sizeOfCentralDirectory = [data zk_hostInt64OffsetBy:&offset];
44 | record.offsetOfStartOfCentralDirectory = [data zk_hostInt64OffsetBy:&offset];
45 | return record;
46 | }
47 |
48 | + (ZKCDTrailer64 *)recordWithArchivePath:(NSString *)path atOffset:(UInt64)offset {
49 | NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
50 | [file seekToFileOffset:offset];
51 | NSData *data = [file readDataOfLength:ZKCDTrailer64FixedDataLength];
52 | [file closeFile];
53 | ZKCDTrailer64 *record = [self recordWithData:data atOffset:0];
54 | return record;
55 | }
56 |
57 | - (NSData *)data {
58 | NSMutableData *data = [NSMutableData zk_dataWithLittleInt32:self.magicNumber];
59 | [data zk_appendLittleInt64:self.sizeOfTrailer];
60 | [data zk_appendLittleInt16:self.versionMadeBy];
61 | [data zk_appendLittleInt16:self.versionNeededToExtract];
62 | [data zk_appendLittleInt32:self.thisDiskNumber];
63 | [data zk_appendLittleInt32:self.diskNumberWithStartOfCentralDirectory];
64 | [data zk_appendLittleInt64:self.numberOfCentralDirectoryEntriesOnThisDisk];
65 | [data zk_appendLittleInt64:self.totalNumberOfCentralDirectoryEntries];
66 | [data zk_appendLittleInt64:self.sizeOfCentralDirectory];
67 | [data zk_appendLittleInt64:self.offsetOfStartOfCentralDirectory];
68 | return data;
69 | }
70 |
71 | - (NSUInteger)length {
72 | return ZKCDTrailer64FixedDataLength;
73 | }
74 |
75 | - (NSString *)description {
76 | return [NSString stringWithFormat:@"%qu entries @ offset of CD: %qu (%qu bytes)",
77 | self.numberOfCentralDirectoryEntriesOnThisDisk,
78 | self.offsetOfStartOfCentralDirectory,
79 | self.sizeOfCentralDirectory];
80 | }
81 |
82 | @end
83 |
--------------------------------------------------------------------------------
/ZipKit/ZKCDTrailer64Locator.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKCDTrailer64Locator.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface ZKCDTrailer64Locator : NSObject
11 |
12 | + (ZKCDTrailer64Locator *)recordWithData:(NSData *)data atOffset:(UInt64)offset;
13 | + (ZKCDTrailer64Locator *)recordWithArchivePath:(NSString *)path andCDTrailerLength:(NSUInteger)cdTrailerLength;
14 |
15 | - (NSData *)data;
16 | - (NSUInteger)length;
17 |
18 | @property (assign) UInt32 magicNumber;
19 | @property (assign) UInt32 diskNumberWithStartOfCentralDirectory;
20 | @property (assign) UInt64 offsetOfStartOfCentralDirectoryTrailer64;
21 | @property (assign) UInt32 numberOfDisks;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/ZipKit/ZKCDTrailer64Locator.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKCDTrailer64Locator.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "ZKCDTrailer64Locator.h"
9 | #import "NSData+ZKAdditions.h"
10 | #import "ZKDefs.h"
11 |
12 | @implementation ZKCDTrailer64Locator
13 |
14 | - (id)init {
15 | if (self = [super init]) {
16 | self.magicNumber = ZKCDTrailer64LocatorMagicNumber;
17 | self.diskNumberWithStartOfCentralDirectory = 0;
18 | self.numberOfDisks = 1;
19 | }
20 | return self;
21 | }
22 |
23 | + (ZKCDTrailer64Locator *)recordWithData:(NSData *)data atOffset:(UInt64)offset {
24 | if (!data || data.length < 1) {
25 | return nil;
26 | }
27 | UInt32 mn = [data zk_hostInt32OffsetBy:&offset];
28 | if (mn != ZKCDTrailer64LocatorMagicNumber) {
29 | return nil;
30 | }
31 | ZKCDTrailer64Locator *record = [ZKCDTrailer64Locator new];
32 | record.magicNumber = mn;
33 | record.diskNumberWithStartOfCentralDirectory = [data zk_hostInt32OffsetBy:&offset];
34 | record.offsetOfStartOfCentralDirectoryTrailer64 = [data zk_hostInt64OffsetBy:&offset];
35 | record.numberOfDisks = [data zk_hostInt32OffsetBy:&offset];
36 | return record;
37 | }
38 |
39 | + (ZKCDTrailer64Locator *)recordWithArchivePath:(NSString *)path andCDTrailerLength:(NSUInteger)cdTrailerLength {
40 | NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
41 | unsigned long long fileOffset = [file seekToEndOfFile] - cdTrailerLength - ZKCDTrailer64LocatorFixedDataLength;
42 | [file seekToFileOffset:fileOffset];
43 | NSData *data = [file readDataOfLength:ZKCDTrailer64LocatorFixedDataLength];
44 | [file closeFile];
45 | ZKCDTrailer64Locator *record = [self recordWithData:data atOffset:0];
46 | return record;
47 | }
48 |
49 | - (NSData *)data {
50 | NSMutableData *data = [NSMutableData zk_dataWithLittleInt32:self.magicNumber];
51 | [data zk_appendLittleInt32:self.diskNumberWithStartOfCentralDirectory];
52 | [data zk_appendLittleInt64:self.offsetOfStartOfCentralDirectoryTrailer64];
53 | [data zk_appendLittleInt32:self.numberOfDisks];
54 | return data;
55 | }
56 |
57 | - (NSUInteger)length {
58 | return ZKCDTrailer64LocatorFixedDataLength;
59 | }
60 |
61 | - (NSString *)description {
62 | return [NSString stringWithFormat:@"offset of CD64: %qu", self.offsetOfStartOfCentralDirectoryTrailer64];
63 | }
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/ZipKit/ZKDataArchive.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKDataArchive.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 07/05/09.
6 | //
7 |
8 | #import
9 | #import
10 |
11 | @class ZKCDHeader;
12 |
13 | @interface ZKDataArchive : ZKArchive {
14 | NSMutableData *_data;
15 | NSMutableArray *_inflatedFiles;
16 | }
17 |
18 | + (ZKDataArchive *)archiveWithArchivePath:(NSString *)path;
19 | + (ZKDataArchive *)archiveWithArchiveData:(NSMutableData *)archiveData;
20 | - (NSUInteger)inflateAll;
21 | - (NSData *)inflateFile:(ZKCDHeader *)cdHeader attributes:(NSDictionary **)fileAttributes;
22 | - (NSUInteger)inflateInFolder:(NSString *)enclosingFolder withFolderName:(NSString *)folderName usingResourceFork:(BOOL)rfFlag;
23 |
24 | - (NSInteger)deflateFiles:(NSArray *)paths relativeToPath:(NSString *)basePath usingResourceFork:(BOOL)flag;
25 | - (NSInteger)deflateDirectory:(NSString *)dirPath relativeToPath:(NSString *)basePath usingResourceFork:(BOOL)flag;
26 | - (NSInteger)deflateFile:(NSString *)path relativeToPath:(NSString *)basePath usingResourceFork:(BOOL)rfFlag;
27 | - (NSInteger)deflateData:(NSData *)data withFilename:(NSString *)filename andAttributes:(NSDictionary *)fileAttributes;
28 |
29 | @property (strong) NSMutableData *data;
30 | @property (strong) NSMutableArray *inflatedFiles;
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/ZipKit/ZKDefs.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKDefs.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | #define ZK_TARGET_OS_MAC (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
11 | #define ZK_TARGET_OS_IPHONE (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_IPHONE_SIMULATOR)
12 |
13 | enum ZKReturnCodes {
14 | zkFailed = -1,
15 | zkCancelled = 0,
16 | zkSucceeded = 1,
17 | };
18 |
19 | // File & path naming
20 | extern NSString *const ZKArchiveFileExtension;
21 | extern NSString *const ZKMacOSXDirectory;
22 | extern NSString *const ZKDotUnderscore;
23 | extern NSString *const ZKExpansionDirectoryName;
24 |
25 | // Keys for dictionary passed to size calculation thread
26 | extern NSString *const ZKPathsKey;
27 | extern NSString *const ZKusingResourceForkKey;
28 |
29 | // Keys for dictionary returned from ZKDataArchive inflation
30 | extern NSString *const ZKFileDataKey;
31 | extern NSString *const ZKFileAttributesKey;
32 | extern NSString *const ZKPathKey;
33 |
34 | // Zipping & Unzipping
35 | extern const unsigned long long ZKZipBlockSize;
36 | extern const UInt32 ZKNotificationIterations;
37 |
38 | // Magic numbers and lengths for zip records
39 | extern const UInt32 ZKCDHeaderMagicNumber;
40 | extern const UInt32 ZKCDHeaderFixedDataLength;
41 |
42 | extern const UInt32 ZKCDTrailerMagicNumber;
43 | extern const UInt32 ZKCDTrailerFixedDataLength;
44 |
45 | extern const UInt32 ZKLFHeaderMagicNumber;
46 | extern const UInt32 ZKLFHeaderFixedDataLength;
47 |
48 | extern const UInt32 ZKCDTrailer64MagicNumber;
49 | extern const UInt32 ZKCDTrailer64FixedDataLength;
50 |
51 | extern const UInt32 ZKCDTrailer64LocatorMagicNumber;
52 | extern const UInt32 ZKCDTrailer64LocatorFixedDataLength;
53 |
--------------------------------------------------------------------------------
/ZipKit/ZKDefs.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKDefs.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "ZKDefs.h"
9 |
10 | NSString *const ZKArchiveFileExtension = @"zip";
11 | NSString *const ZKMacOSXDirectory = @"__MACOSX";
12 | NSString *const ZKDotUnderscore = @"._";
13 | NSString *const ZKExpansionDirectoryName = @".ZipKit";
14 |
15 | NSString *const ZKPathsKey = @"paths";
16 | NSString *const ZKusingResourceForkKey = @"usingResourceFork";
17 |
18 | NSString *const ZKFileDataKey = @"fileData";
19 | NSString *const ZKFileAttributesKey = @"fileAttributes";
20 | NSString *const ZKPathKey = @"path";
21 |
22 | const unsigned long long ZKZipBlockSize = 262144;
23 | const UInt32 ZKNotificationIterations = 100;
24 |
25 | const UInt32 ZKCDHeaderMagicNumber = 0x02014B50;
26 | const UInt32 ZKCDHeaderFixedDataLength = 46;
27 |
28 | const UInt32 ZKCDTrailerMagicNumber = 0x06054B50;
29 | const UInt32 ZKCDTrailerFixedDataLength = 22;
30 |
31 | const UInt32 ZKLFHeaderMagicNumber = 0x04034B50;
32 | const UInt32 ZKLFHeaderFixedDataLength = 30;
33 |
34 | const UInt32 ZKCDTrailer64MagicNumber = 0x06064b50;
35 | const UInt32 ZKCDTrailer64FixedDataLength = 56;
36 |
37 | const UInt32 ZKCDTrailer64LocatorMagicNumber = 0x07064b50;
38 | const UInt32 ZKCDTrailer64LocatorFixedDataLength = 20;
39 |
--------------------------------------------------------------------------------
/ZipKit/ZKFileArchive.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKFileArchive.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 | #import
10 |
11 | @class ZKCDHeader;
12 |
13 | @interface ZKFileArchive : ZKArchive
14 |
15 | + (ZKFileArchive *)process:(id)item usingResourceFork:(BOOL)flag withInvoker:(id)invoker andDelegate:(id)delegate;
16 | + (ZKFileArchive *)archiveWithArchivePath:(NSString *)archivePath;
17 |
18 | - (NSInteger)inflateToDiskUsingResourceFork:(BOOL)flag;
19 | - (NSInteger)inflateToDirectory:(NSString *)expansionDirectory usingResourceFork:(BOOL)rfFlag;
20 | - (NSInteger)inflateFile:(ZKCDHeader *)cdHeader toDirectory:(NSString *)expansionDirectory;
21 |
22 | - (NSInteger)deflateFiles:(NSArray *)paths relativeToPath:(NSString *)basePath usingResourceFork:(BOOL)flag;
23 | - (NSInteger)deflateDirectory:(NSString *)dirPath relativeToPath:(NSString *)basePath usingResourceFork:(BOOL)flag;
24 | - (NSInteger)deflateFile:(NSString *)path relativeToPath:(NSString *)basePath usingResourceFork:(BOOL)flag;
25 |
26 | @property (assign) BOOL useZip64Extensions;
27 | @property (atomic) int compressionLevel;
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/ZipKit/ZKLFHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKLFHeader.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | @interface ZKLFHeader : NSObject
11 |
12 | + (ZKLFHeader *)recordWithData:(NSData *)data atOffset:(UInt64)offset;
13 | + (ZKLFHeader *)recordWithArchivePath:(NSString *)path atOffset:(UInt64)offset;
14 | - (void)parseZip64ExtraField;
15 | - (NSData *)zip64ExtraField;
16 | - (NSData *)data;
17 | - (NSUInteger)length;
18 | - (BOOL)useZip64Extensions;
19 | - (BOOL)isResourceFork;
20 |
21 | @property (assign) UInt32 magicNumber;
22 | @property (assign) UInt32 versionNeededToExtract;
23 | @property (assign) UInt32 generalPurposeBitFlag;
24 | @property (assign) UInt32 compressionMethod;
25 | @property (strong) NSDate *lastModDate;
26 | @property (assign) UInt32 crc;
27 | @property (assign) UInt64 compressedSize;
28 | @property (assign) UInt64 uncompressedSize;
29 | @property (assign) UInt32 filenameLength;
30 | @property (assign) UInt32 extraFieldLength;
31 | @property (copy) NSString *filename;
32 | @property (strong) NSData *extraField;
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/ZipKit/ZKLFHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKLFHeader.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "ZKLFHeader.h"
9 | #import "NSDate+ZKAdditions.h"
10 | #import "NSData+ZKAdditions.h"
11 | #import "NSString+ZKAdditions.h"
12 | #import "ZKDefs.h"
13 | #import "zlib.h"
14 |
15 | @implementation ZKLFHeader
16 |
17 | - (id)init {
18 | if (self = [super init]) {
19 | self.magicNumber = ZKLFHeaderMagicNumber;
20 | self.versionNeededToExtract = 20;
21 | self.generalPurposeBitFlag = 0;
22 | self.compressionMethod = Z_DEFLATED;
23 | self.lastModDate = [NSDate date];
24 | self.crc = 0;
25 | self.compressedSize = 0;
26 | self.uncompressedSize = 0;
27 | self.filenameLength = 0;
28 | self.extraFieldLength = 0;
29 | self.filename = nil;
30 | self.extraField = nil;
31 |
32 | [self addObserver:self forKeyPath:@"compressedSize" options:NSKeyValueObservingOptionNew context:nil];
33 | [self addObserver:self forKeyPath:@"uncompressedSize" options:NSKeyValueObservingOptionNew context:nil];
34 | [self addObserver:self forKeyPath:@"extraField" options:NSKeyValueObservingOptionNew context:nil];
35 | [self addObserver:self forKeyPath:@"filename" options:NSKeyValueObservingOptionNew context:nil];
36 | }
37 | return self;
38 | }
39 |
40 | - (void)removeObservers {
41 | [self removeObserver:self forKeyPath:@"compressedSize"];
42 | [self removeObserver:self forKeyPath:@"uncompressedSize"];
43 | [self removeObserver:self forKeyPath:@"extraField"];
44 | [self removeObserver:self forKeyPath:@"filename"];
45 | }
46 |
47 | - (void)dealloc {
48 | [self removeObservers];
49 | }
50 |
51 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
52 | if ([keyPath isEqualToString:@"compressedSize"] || [keyPath isEqualToString:@"uncompressedSize"]) {
53 | self.versionNeededToExtract = ([self useZip64Extensions] ? 45 : 20);
54 | } else if ([keyPath isEqualToString:@"extraField"] && self.extraFieldLength < 1) {
55 | self.extraFieldLength = (UInt32)[self.extraField length];
56 | } else if ([keyPath isEqualToString:@"filename"] && self.filenameLength < 1) {
57 | self.filenameLength = [self.filename zk_precomposedUTF8Length];
58 | }
59 | }
60 |
61 | + (ZKLFHeader *)recordWithData:(NSData *)data atOffset:(UInt64)offset {
62 | if (!data || data.length < 1) {
63 | return nil;
64 | }
65 | UInt32 mn = [data zk_hostInt32OffsetBy:&offset];
66 | if (mn != ZKLFHeaderMagicNumber) {
67 | return nil;
68 | }
69 | ZKLFHeader *record = [ZKLFHeader new];
70 | record.magicNumber = mn;
71 | record.versionNeededToExtract = [data zk_hostInt16OffsetBy:&offset];
72 | record.generalPurposeBitFlag = [data zk_hostInt16OffsetBy:&offset];
73 | record.compressionMethod = [data zk_hostInt16OffsetBy:&offset];
74 | record.lastModDate = [NSDate zk_dateWithDosDate:[data zk_hostInt32OffsetBy:&offset]];
75 | record.crc = [data zk_hostInt32OffsetBy:&offset];
76 | record.compressedSize = [data zk_hostInt32OffsetBy:&offset];
77 | record.uncompressedSize = [data zk_hostInt32OffsetBy:&offset];
78 | record.filenameLength = [data zk_hostInt16OffsetBy:&offset];
79 | record.extraFieldLength = [data zk_hostInt16OffsetBy:&offset];
80 | if ([data length] > ZKLFHeaderFixedDataLength) {
81 | if (record.filenameLength > 0) {
82 | record.filename = [data zk_stringOffsetBy:&offset length:record.filenameLength];
83 | }
84 | if (record.extraFieldLength > 0) {
85 | record.extraField = [data subdataWithRange:NSMakeRange((NSUInteger)offset, record.extraFieldLength)];
86 | [record parseZip64ExtraField];
87 | }
88 | }
89 | return record;
90 | }
91 |
92 | + (ZKLFHeader *)recordWithArchivePath:(NSString *)path atOffset:(UInt64)offset {
93 | NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
94 | [file seekToFileOffset:offset];
95 | NSData *fixedData = [file readDataOfLength:ZKLFHeaderFixedDataLength];
96 | ZKLFHeader *record = [self recordWithData:fixedData atOffset:0];
97 | if (record.filenameLength > 0) {
98 | NSData *data = [file readDataOfLength:record.filenameLength];
99 | record.filename = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
100 | }
101 | if (record.extraFieldLength > 0) {
102 | record.extraField = [file readDataOfLength:record.extraFieldLength];
103 | [record parseZip64ExtraField];
104 | }
105 | [file closeFile];
106 | return record;
107 | }
108 |
109 | - (NSData *)data {
110 | self.extraField = [self zip64ExtraField];
111 |
112 | NSMutableData *data = [NSMutableData zk_dataWithLittleInt32:self.magicNumber];
113 | [data zk_appendLittleInt16:self.versionNeededToExtract];
114 | [data zk_appendLittleInt16:self.generalPurposeBitFlag];
115 | [data zk_appendLittleInt16:self.compressionMethod];
116 | [data zk_appendLittleInt32:[self.lastModDate zk_dosDate]];
117 | [data zk_appendLittleInt32:self.crc];
118 | if ([self useZip64Extensions]) {
119 | [data zk_appendLittleInt32:0xFFFFFFFF];
120 | [data zk_appendLittleInt32:0xFFFFFFFF];
121 | } else {
122 | [data zk_appendLittleInt32:(UInt32)self.compressedSize];
123 | [data zk_appendLittleInt32:(UInt32)self.uncompressedSize];
124 | }
125 | [data zk_appendLittleInt16:self.filenameLength];
126 | [data zk_appendLittleInt16:[self.extraField length]];
127 | [data zk_appendPrecomposedUTF8String:self.filename];
128 | [data appendData:self.extraField];
129 | return data;
130 | }
131 |
132 | - (void)parseZip64ExtraField {
133 | NSUInteger tag, length;
134 | UInt64 offset = 0;
135 | while (offset < self.extraFieldLength) {
136 | tag = [self.extraField zk_hostInt16OffsetBy:&offset];
137 | length = [self.extraField zk_hostInt16OffsetBy:&offset];
138 | if (tag == 0x0001) {
139 | if (length >= 8) {
140 | self.uncompressedSize = [self.extraField zk_hostInt64OffsetBy:&offset];
141 | }
142 | if (length >= 16) {
143 | self.compressedSize = [self.extraField zk_hostInt64OffsetBy:&offset];
144 | }
145 | break;
146 | } else {
147 | offset += length;
148 | }
149 | }
150 | }
151 |
152 | - (NSData *)zip64ExtraField {
153 | NSMutableData *zip64ExtraField = nil;
154 | if ([self useZip64Extensions]) {
155 | zip64ExtraField = [NSMutableData zk_dataWithLittleInt16:0x0001];
156 | [zip64ExtraField zk_appendLittleInt16:16];
157 | [zip64ExtraField zk_appendLittleInt64:self.uncompressedSize];
158 | [zip64ExtraField zk_appendLittleInt64:self.compressedSize];
159 | }
160 | return zip64ExtraField;
161 | }
162 |
163 | - (NSUInteger)length {
164 | if (!self.extraField || [self.extraField length] == 0) {
165 | self.extraField = [self zip64ExtraField];
166 | }
167 | return ZKLFHeaderFixedDataLength + self.filenameLength + [self.extraField length];
168 | }
169 |
170 | - (BOOL)useZip64Extensions {
171 | return (self.uncompressedSize >= 0xFFFFFFFF) || (self.compressedSize >= 0xFFFFFFFF);
172 | }
173 |
174 | - (NSString *)description {
175 | return [NSString stringWithFormat:@"%@ modified %@, %qu bytes (%qu compressed)",
176 | self.filename, self.lastModDate, self.uncompressedSize, self.compressedSize];
177 | }
178 |
179 | - (BOOL)isResourceFork {
180 | return [self.filename zk_isResourceForkPath];
181 | }
182 |
183 | @end
184 |
--------------------------------------------------------------------------------
/ZipKit/ZKLog.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKLog.h
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import
9 |
10 | enum ZKLogLevel {
11 | ZKLogLevelNotice = 3,
12 | ZKLogLevelError = 2,
13 | ZKLogLevelDebug = 1,
14 | ZKLogLevelAll = 0,
15 | };
16 |
17 | #define ZKLog(s, l, ...) [[ZKLog sharedInstance] logFile:__FILE__ lineNumber: __LINE__ level: l format: (s), ## __VA_ARGS__]
18 |
19 | #define ZKLogError(s, ...) ZKLog((s), ZKLogLevelError, ## __VA_ARGS__)
20 | #define ZKLogNotice(s, ...) ZKLog((s), ZKLogLevelNotice, ## __VA_ARGS__)
21 | #define ZKLogDebug(s, ...) ZKLog((s), ZKLogLevelDebug, ## __VA_ARGS__)
22 |
23 | #define ZKLogWithException(e) ZKLogError(@"Exception in %@: \n\tname: %@\n\treason: %@\n\tuserInfo: %@", NSStringFromSelector(_cmd), [e name], [e reason], [e userInfo]);
24 | #define ZKLogWithError(e) ZKLogError(@"Error in %@: \n\tdomain: %@\n\tcode: %@\n\tdescription: %@", NSStringFromSelector(_cmd), [e domain], [e code], [e localizedDescription]);
25 |
26 | #define ZKStringFromBOOL(b) (b ? @"YES" : @"NO")
27 |
28 | extern NSString *const ZKLogLevelKey;
29 | extern NSString *const ZKLogToFileKey;
30 |
31 | @interface ZKLog : NSObject {
32 | @private
33 | NSUInteger _minimumLevel;
34 | }
35 |
36 | - (void)logFile:(char *)sourceFile lineNumber:(NSUInteger)lineNumber level:(NSUInteger)level format:(NSString *)format, ...;
37 |
38 | - (NSString *)levelToLabel:(NSUInteger)level;
39 |
40 | + (ZKLog *)sharedInstance;
41 |
42 | @property (assign) NSUInteger minimumLevel;
43 | @property (strong) NSDateFormatter *dateFormatter;
44 | @property (assign) int pid;
45 | @property (copy) NSString *logFilePath;
46 | @property (assign) FILE *logFilePointer;
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/ZipKit/ZKLog.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKLog.m
3 | // ZipKit
4 | //
5 | // Created by Karl Moskowski on 01/04/09.
6 | //
7 |
8 | #import "ZKLog.h"
9 |
10 | NSString *const ZKLogLevelKey = @"ZKLogLevel";
11 | NSString *const ZKLogToFileKey = @"ZKLogToFile";
12 |
13 | @implementation ZKLog
14 |
15 | - (void)logFile:(char *)sourceFile lineNumber:(NSUInteger)lineNumber level:(NSUInteger)level format:(NSString *)format, ... {
16 | if (level >= self.minimumLevel) {
17 | va_list args;
18 | va_start(args, format);
19 | NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
20 | va_end(args);
21 | NSString *label = [self levelToLabel:level];
22 | NSString *now = [self.dateFormatter stringFromDate:[NSDate date]];
23 | if (label) {
24 | NSString *line = [NSString stringWithFormat:@"%@ [%i] %@ %@ (%@:%lu)", now, self.pid, label, message, [@(sourceFile)lastPathComponent], (unsigned long)lineNumber];
25 | fprintf(stderr, "%s\n", [line UTF8String]);
26 | fflush(stderr);
27 | }
28 | }
29 | return;
30 | }
31 |
32 | - (NSUInteger)minimumLevel {
33 | return _minimumLevel;
34 | }
35 | - (void)setMinimumLevel:(NSUInteger)value {
36 | switch (value) {
37 | case ZKLogLevelError:
38 | case ZKLogLevelNotice:
39 | case ZKLogLevelDebug:
40 | case ZKLogLevelAll:
41 | _minimumLevel = value;
42 | break;
43 | default:
44 | ZKLogError(@"Invalid logging level: %u. Old value %@ unchanged.", value, [self levelToLabel:self.minimumLevel]);
45 | break;
46 | }
47 | return;
48 | }
49 |
50 | - (NSString *)levelToLabel:(NSUInteger)level {
51 | NSString *label = nil;
52 | switch (level) {
53 | case ZKLogLevelError:
54 | label = @"";
55 | break;
56 | case ZKLogLevelNotice:
57 | label = @"";
58 | break;
59 | case ZKLogLevelDebug:
60 | label = @"";
61 | break;
62 | default:
63 | label = nil;
64 | break;
65 | }
66 | return label;
67 | }
68 |
69 | static ZKLog *zkSharedInstance = nil;
70 | + (ZKLog *)sharedInstance {
71 | @synchronized(self) {
72 | if (zkSharedInstance == nil) {
73 | zkSharedInstance = [self new];
74 | }
75 | }
76 | return zkSharedInstance;
77 | }
78 |
79 | - (id)init {
80 | @synchronized([self class]) {
81 | if (zkSharedInstance == nil) {
82 | if (self = [super init]) {
83 | zkSharedInstance = self;
84 |
85 | self.pid = [[NSProcessInfo processInfo] processIdentifier];
86 | self.minimumLevel = ZKLogLevelError;
87 | self.dateFormatter = [NSDateFormatter new];
88 | [self.dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
89 |
90 | if ([[NSUserDefaults standardUserDefaults] boolForKey:ZKLogToFileKey]) {
91 | NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
92 | NSString *libraryFolder = searchPaths[0];
93 | NSString *logFolder = [libraryFolder stringByAppendingPathComponent:@"Logs"];
94 | [[NSFileManager new] createDirectoryAtPath:logFolder withIntermediateDirectories:YES attributes:nil error:nil];
95 | self.logFilePath = [logFolder stringByAppendingPathComponent:
96 | [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]
97 | stringByAppendingPathExtension:@"log"]];
98 | freopen([self.logFilePath fileSystemRepresentation], "a+", stderr);
99 | }
100 | }
101 | }
102 | }
103 | return zkSharedInstance;
104 | }
105 |
106 | + (id)allocWithZone:(NSZone *)zone {
107 | @synchronized(self) {
108 | if (zkSharedInstance == nil) {
109 | return [super allocWithZone:zone];
110 | }
111 | }
112 | return zkSharedInstance;
113 | }
114 |
115 | + (void)initialize {
116 | [[NSUserDefaults standardUserDefaults] registerDefaults:
117 | @{ZKLogToFileKey: @NO}];
118 | [super initialize];
119 | }
120 |
121 | - (id)copyWithZone:(NSZone *)zone {
122 | return self;
123 | }
124 |
125 |
126 | - (void)dealloc {
127 | if (self.logFilePointer) {
128 | fclose(self.logFilePointer);
129 | }
130 | }
131 |
132 | @dynamic minimumLevel;
133 |
134 | @end
135 |
--------------------------------------------------------------------------------
/ZipKit/ZipKit-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | NSHumanReadableCopyright
26 | Copyright © 2020 Karl Moskowski. All rights reserved.
27 | NSPrincipalClass
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/ZipKit/ZipKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZipKit.h
3 | // ZipKit
4 | //
5 | // Created by Sam Deane on 25/10/13.
6 | //
7 |
8 | #import
9 | #import
10 | #import
11 | #import
12 | #import
13 |
14 | #import
15 | #import
16 | #import
17 | #import
18 | #import
19 | #import
20 |
21 | #if ZK_TARGET_OS_MAC
22 | #import
23 | #import
24 | #endif
25 |
--------------------------------------------------------------------------------