cog/Frameworks/Sparkle/SUUnarchiver.m

144 lines
4.4 KiB
Objective-C
Executable File

//
// 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);
if ((cmdFP = popen([command UTF8String], "w")))
{
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