| 1 | // |
|---|
| 2 | // SUPipedUnarchiver.m |
|---|
| 3 | // Sparkle |
|---|
| 4 | // |
|---|
| 5 | // Created by Andy Matuschak on 6/16/08. |
|---|
| 6 | // Copyright 2008 Andy Matuschak. All rights reserved. |
|---|
| 7 | // |
|---|
| 8 | |
|---|
| 9 | #import "SUPipedUnarchiver.h" |
|---|
| 10 | #import "SUUnarchiver_Private.h" |
|---|
| 11 | |
|---|
| 12 | @implementation SUPipedUnarchiver |
|---|
| 13 | |
|---|
| 14 | + (SEL)_selectorConformingToTypeOfPath:(NSString *)path |
|---|
| 15 | { |
|---|
| 16 | static NSDictionary *typeSelectorDictionary; |
|---|
| 17 | if (!typeSelectorDictionary) |
|---|
| 18 | typeSelectorDictionary = [[NSDictionary dictionaryWithObjectsAndKeys:@"_extractZIP", @".zip", @"_extractTAR", @".tar", |
|---|
| 19 | @"_extractTGZ", @".tar.gz", @"_extractTGZ", @".tgz", |
|---|
| 20 | @"_extractTBZ", @".tar.bz2", @"_extractTBZ", @".tbz", nil] retain]; |
|---|
| 21 | |
|---|
| 22 | NSString *lastPathComponent = [path lastPathComponent]; |
|---|
| 23 | NSEnumerator *typeEnumerator = [typeSelectorDictionary keyEnumerator]; |
|---|
| 24 | id currentType; |
|---|
| 25 | while ((currentType = [typeEnumerator nextObject])) |
|---|
| 26 | { |
|---|
| 27 | if ([currentType length] > [lastPathComponent length]) continue; |
|---|
| 28 | if ([[lastPathComponent substringFromIndex:[lastPathComponent length] - [currentType length]] isEqualToString:currentType]) |
|---|
| 29 | return NSSelectorFromString([typeSelectorDictionary objectForKey:currentType]); |
|---|
| 30 | } |
|---|
| 31 | return NULL; |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | - (void)start |
|---|
| 35 | { |
|---|
| 36 | [NSThread detachNewThreadSelector:[[self class] _selectorConformingToTypeOfPath:archivePath] toTarget:self withObject:nil]; |
|---|
| 37 | } |
|---|
| 38 | |
|---|
| 39 | + (BOOL)_canUnarchivePath:(NSString *)path |
|---|
| 40 | { |
|---|
| 41 | return ([self _selectorConformingToTypeOfPath:path] != nil); |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | // This method abstracts the types that use a command line tool piping data from stdin. |
|---|
| 45 | - (void)_extractArchivePipingDataToCommand:(NSString *)command |
|---|
| 46 | { |
|---|
| 47 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|---|
| 48 | FILE *fp = NULL, *cmdFP = NULL; |
|---|
| 49 | |
|---|
| 50 | // Get the file size. |
|---|
| 51 | NSNumber *fs = [[[NSFileManager defaultManager] fileAttributesAtPath:archivePath traverseLink:NO] objectForKey:NSFileSize]; |
|---|
| 52 | if (fs == nil) goto reportError; |
|---|
| 53 | |
|---|
| 54 | // Thank you, Allan Odgaard! |
|---|
| 55 | // (who wrote the following extraction alg.) |
|---|
| 56 | fp = fopen([archivePath fileSystemRepresentation], "r"); |
|---|
| 57 | if (!fp) goto reportError; |
|---|
| 58 | |
|---|
| 59 | setenv("DESTINATION", [[archivePath stringByDeletingLastPathComponent] fileSystemRepresentation], 1); |
|---|
| 60 | cmdFP = popen([command fileSystemRepresentation], "w"); |
|---|
| 61 | if (!cmdFP) goto reportError; |
|---|
| 62 | |
|---|
| 63 | char buf[32*1024]; |
|---|
| 64 | long len; |
|---|
| 65 | while((len = fread(buf, 1, 32*1024, fp))) |
|---|
| 66 | { |
|---|
| 67 | fwrite(buf, 1, len, cmdFP); |
|---|
| 68 | [self performSelectorOnMainThread:@selector(_notifyDelegateOfExtractedLength:) withObject:[NSNumber numberWithLong:len] waitUntilDone:NO]; |
|---|
| 69 | } |
|---|
| 70 | pclose(cmdFP); |
|---|
| 71 | |
|---|
| 72 | [self performSelectorOnMainThread:@selector(_notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO]; |
|---|
| 73 | goto finally; |
|---|
| 74 | |
|---|
| 75 | reportError: |
|---|
| 76 | [self performSelectorOnMainThread:@selector(_notifyDelegateOfFailure) withObject:nil waitUntilDone:NO]; |
|---|
| 77 | |
|---|
| 78 | finally: |
|---|
| 79 | if (fp) |
|---|
| 80 | fclose(fp); |
|---|
| 81 | [pool drain]; |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | - (void)_extractTAR |
|---|
| 85 | { |
|---|
| 86 | return [self _extractArchivePipingDataToCommand:@"tar -xC \"$DESTINATION\""]; |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | - (void)_extractTGZ |
|---|
| 90 | { |
|---|
| 91 | return [self _extractArchivePipingDataToCommand:@"tar -zxC \"$DESTINATION\""]; |
|---|
| 92 | } |
|---|
| 93 | |
|---|
| 94 | - (void)_extractTBZ |
|---|
| 95 | { |
|---|
| 96 | return [self _extractArchivePipingDataToCommand:@"tar -jxC \"$DESTINATION\""]; |
|---|
| 97 | } |
|---|
| 98 | |
|---|
| 99 | - (void)_extractZIP |
|---|
| 100 | { |
|---|
| 101 | return [self _extractArchivePipingDataToCommand:@"ditto -x -k - \"$DESTINATION\""]; |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | + (void)load |
|---|
| 105 | { |
|---|
| 106 | [self _registerImplementation:self]; |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | @end |
|---|