2007-11-04 03:44:15 +00:00
|
|
|
//
|
|
|
|
// SUUnarchiver.m
|
|
|
|
// Sparkle
|
|
|
|
//
|
|
|
|
// Created by Andy Matuschak on 3/16/06.
|
|
|
|
// Copyright 2006 Andy Matuschak. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "SUUnarchiver.h"
|
|
|
|
|
|
|
|
|
|
|
|
@implementation SUUnarchiver
|
|
|
|
|
|
|
|
// This method abstracts the types that use a command line tool piping data from stdin.
|
|
|
|
- (BOOL)_extractArchivePath:archivePath pipingDataToCommand:(NSString *)command
|
|
|
|
{
|
|
|
|
// Get the file size.
|
|
|
|
NSNumber *fs = [[[NSFileManager defaultManager] fileAttributesAtPath:archivePath traverseLink:NO] objectForKey:NSFileSize];
|
|
|
|
if (fs == nil) { return NO; }
|
|
|
|
|
|
|
|
// Thank you, Allan Odgaard!
|
|
|
|
// (who wrote the following extraction alg.)
|
|
|
|
|
|
|
|
long current = 0;
|
|
|
|
FILE *fp, *cmdFP;
|
|
|
|
sig_t oldSigPipeHandler = signal(SIGPIPE, SIG_IGN);
|
|
|
|
if ((fp = fopen([archivePath UTF8String], "r")))
|
|
|
|
{
|
|
|
|
setenv("DESTINATION", [[archivePath stringByDeletingLastPathComponent] UTF8String], 1);
|
2009-08-16 17:56:49 +00:00
|
|
|
if ((cmdFP = popen([command UTF8String], "w")))
|
2007-11-04 03:44:15 +00:00
|
|
|
{
|
|
|
|
char buf[32*1024];
|
|
|
|
long len;
|
|
|
|
while((len = fread(buf, 1, 32 * 1024, fp)))
|
|
|
|
{
|
|
|
|
current += len;
|
|
|
|
|
|
|
|
NSEvent *event;
|
|
|
|
while((event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]))
|
|
|
|
[NSApp sendEvent:event];
|
|
|
|
|
|
|
|
fwrite(buf, 1, len, cmdFP);
|
|
|
|
|
|
|
|
if ([delegate respondsToSelector:@selector(unarchiver:extractedLength:)])
|
|
|
|
[delegate unarchiver:self extractedLength:len];
|
|
|
|
}
|
|
|
|
pclose(cmdFP);
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
signal(SIGPIPE, oldSigPipeHandler);
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)_extractTAR:(NSString *)archivePath
|
|
|
|
{
|
|
|
|
return [self _extractArchivePath:archivePath pipingDataToCommand:@"tar -xC \"$DESTINATION\""];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)_extractTGZ:(NSString *)archivePath
|
|
|
|
{
|
|
|
|
return [self _extractArchivePath:archivePath pipingDataToCommand:@"tar -zxC \"$DESTINATION\""];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)_extractTBZ:(NSString *)archivePath
|
|
|
|
{
|
|
|
|
return [self _extractArchivePath:archivePath pipingDataToCommand:@"tar -jxC \"$DESTINATION\""];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)_extractZIP:(NSString *)archivePath
|
|
|
|
{
|
|
|
|
return [self _extractArchivePath:archivePath pipingDataToCommand:@"ditto -x -k - \"$DESTINATION\""];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)_extractDMG:(NSString *)archivePath
|
|
|
|
{
|
|
|
|
sig_t oldSigChildHandler = signal(SIGCHLD, SIG_DFL);
|
|
|
|
// First, we internet-enable the volume.
|
|
|
|
NSTask *hdiTask = [NSTask launchedTaskWithLaunchPath:@"/usr/bin/env" arguments:[NSArray arrayWithObjects:@"hdiutil", @"internet-enable", @"-quiet", archivePath, nil]];
|
|
|
|
[hdiTask waitUntilExit];
|
|
|
|
if ([hdiTask terminationStatus] != 0) { return NO; }
|
|
|
|
|
|
|
|
// Now, open the volume; it'll extract into its own directory.
|
|
|
|
hdiTask = [NSTask launchedTaskWithLaunchPath:@"/usr/bin/env" arguments:[NSArray arrayWithObjects:@"hdiutil", @"attach", @"-idme", @"-noidmereveal", @"-noidmetrash", @"-noverify", @"-nobrowse", @"-noautoopen", @"-quiet", archivePath, nil]];
|
|
|
|
[hdiTask waitUntilExit];
|
|
|
|
if ([hdiTask terminationStatus] != 0) { return NO; }
|
|
|
|
|
|
|
|
signal(SIGCHLD, oldSigChildHandler);
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)_unarchivePath:(NSString *)path
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
|
|
|
// This dictionary associates names of methods responsible for extraction with file extensions.
|
|
|
|
// The methods take the path of the archive to extract. They return a BOOL indicating whether
|
|
|
|
// we should continue with the update; returns NO if an error occurred.
|
|
|
|
NSDictionary *commandDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
@"_extractTBZ:", @"tbz",
|
|
|
|
@"_extractTGZ:", @"tgz",
|
|
|
|
@"_extractTAR:", @"tar",
|
|
|
|
@"_extractZIP:", @"zip",
|
|
|
|
@"_extractDMG:", @"dmg", nil];
|
|
|
|
SEL command = NSSelectorFromString([commandDictionary objectForKey:[path pathExtension]]);
|
|
|
|
|
|
|
|
BOOL result;
|
|
|
|
if (command)
|
|
|
|
{
|
|
|
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:command]];
|
|
|
|
[invocation setSelector:command];
|
|
|
|
[invocation setArgument:&path atIndex:2]; // 0 and 1 are private!
|
|
|
|
[invocation invokeWithTarget:self];
|
|
|
|
[invocation getReturnValue:&result];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result = NO;
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
if ([delegate respondsToSelector:@selector(unarchiverDidFinish:)])
|
|
|
|
[delegate performSelector:@selector(unarchiverDidFinish:) withObject:self];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ([delegate respondsToSelector:@selector(unarchiverDidFail:)])
|
|
|
|
[delegate performSelector:@selector(unarchiverDidFail:) withObject:self];
|
|
|
|
}
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)unarchivePath:(NSString *)path
|
|
|
|
{
|
|
|
|
[NSThread detachNewThreadSelector:@selector(_unarchivePath:) toTarget:self withObject:path];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setDelegate:del
|
|
|
|
{
|
|
|
|
delegate = del;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|