10
10
#import "Sparkle.h"
11
11
#import "SUUnarchiver.h"
12
#import "SUUnarchiver_Private.h"
13
14
@implementation SUUnarchiver
15
// This method abstracts the types that use a command line tool piping data from stdin.
16
- (BOOL)_extractArchivePipingDataToCommand:(NSString *)command
19
NSNumber *fs = [[[NSFileManager defaultManager] fileAttributesAtPath:archivePath traverseLink:NO] objectForKey:NSFileSize];
20
if (fs == nil) { return NO; }
22
// Thank you, Allan Odgaard!
23
// (who wrote the following extraction alg.)
27
if ((fp = fopen([archivePath fileSystemRepresentation], "r")))
29
setenv("DESTINATION", [[archivePath stringByDeletingLastPathComponent] UTF8String], 1);
30
if ((cmdFP = popen([command fileSystemRepresentation], "w")))
34
while((len = fread(buf, 1, 32 * 1024, fp)))
39
while((event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]))
40
[NSApp sendEvent:event];
42
fwrite(buf, 1, len, cmdFP);
44
[self performSelectorOnMainThread:@selector(notifyDelegateOfExtractedLength:) withObject:[NSNumber numberWithLong:len] waitUntilDone:NO];
56
return [self _extractArchivePipingDataToCommand:@"tar -xC \"$DESTINATION\""];
61
return [self _extractArchivePipingDataToCommand:@"tar -zxC \"$DESTINATION\""];
66
return [self _extractArchivePipingDataToCommand:@"tar -jxC \"$DESTINATION\""];
71
return [self _extractArchivePipingDataToCommand:@"ditto -x -k - \"$DESTINATION\""];
76
// get a unique mount point path
77
NSString *mountPoint = [[archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"mp"];
79
while ([[NSFileManager defaultManager] fileExistsAtPath:mountPoint] && cnt <= 999)
80
mountPoint = [[archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:@"mp%d", cnt++]];
82
if (![[NSFileManager defaultManager] fileExistsAtPath:mountPoint])
84
// create mount point folder
85
[[NSFileManager defaultManager] createDirectoryAtPath:mountPoint attributes:nil];
87
if ([[NSFileManager defaultManager] fileExistsAtPath:mountPoint])
89
NSArray* arguments = [NSArray arrayWithObjects:@"attach", archivePath, @"-mountpoint", mountPoint, @"-noverify", @"-nobrowse", @"-noautoopen", nil];
90
// set up a pipe and push "yes" (y works too), this will accept any license agreement crap
91
// not every .dmg needs this, but this will make sure it works with everyone
92
NSData* yesData = [[[NSData alloc] initWithBytes:"yes\n" length:4] autorelease];
94
[NTSynchronousTask task:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:yesData];
103
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
105
// This dictionary associates names of methods responsible for extraction with file extensions.
106
// The methods take the path of the archive to extract. They return a BOOL indicating whether
107
// we should continue with the update; returns NO if an error occurred.
108
NSDictionary *commandDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
109
@"_extractTBZ", @".tbz",
110
@"_extractTBZ", @".tar.bz2",
111
@"_extractTGZ", @".tgz",
112
@"_extractTGZ", @".tar.gz",
113
@"_extractTAR", @".tar",
114
@"_extractZIP", @".zip",
115
@"_extractDMG", @".dmg",
118
NSString *theLastPathComponent = [[archivePath lastPathComponent] lowercaseString];
119
NSEnumerator *theEnumerator = [[commandDictionary allKeys] objectEnumerator];
120
NSString *theExtension = NULL;
121
while ((theExtension = [theEnumerator nextObject]) != NULL)
123
if ([[theLastPathComponent substringFromIndex:[theLastPathComponent length] - [theExtension length]] isEqualToString:theExtension])
125
command = NSSelectorFromString([commandDictionary objectForKey:theExtension]);
133
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:command]];
134
[invocation setSelector:command];
135
[invocation invokeWithTarget:self];
136
[invocation getReturnValue:&result];
142
[self performSelectorOnMainThread:@selector(notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO];
144
[self performSelectorOnMainThread:@selector(notifyDelegateOfFailure) withObject:nil waitUntilDone:NO];
149
- (void)unarchivePath:(NSString *)path
151
archivePath = [path copy];
152
[NSThread detachNewThreadSelector:@selector(_unarchive) toTarget:self withObject:nil];
16
extern NSMutableArray *__unarchiverImplementations;
18
+ (SUUnarchiver *)unarchiverForURL:(NSURL *)URL
20
NSEnumerator *implementationEnumerator = [[self _unarchiverImplementations] objectEnumerator];
22
while ((current = [implementationEnumerator nextObject]))
24
if ([current _canUnarchiveURL:URL])
25
return [[[current alloc] _initWithURL:URL] autorelease];
155
30
- (void)setDelegate:del
162
if ([[archivePath pathExtension] isEqualToString:@".dmg"])
164
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:[NSArray arrayWithObjects:@"detach", [archivePath stringByDeletingLastPathComponent], @"-force", nil]];
166
[[NSFileManager defaultManager] removeFileAtPath:archivePath handler:nil];
171
[archivePath release];
175
- (void)notifyDelegateOfExtractedLength:(long)length
177
if ([delegate respondsToSelector:@selector(unarchiver:extractedLength:)])
178
[delegate unarchiver:self extractedLength:length];
181
- (void)notifyDelegateOfSuccess
183
if ([delegate respondsToSelector:@selector(unarchiverDidFinish:)])
184
[delegate performSelector:@selector(unarchiverDidFinish:) withObject:self];
187
- (void)notifyDelegateOfFailure
189
if ([delegate respondsToSelector:@selector(unarchiverDidFail:)])
190
[delegate performSelector:@selector(unarchiverDidFail:) withObject:self];