1#import <Foundation/Foundation.h> 2#import "SevenZipArchive.h" 3#import "FileInfo.h" 4#import "NSString+Custom.h" 5#import "Preferences.h" 6#import "NSArray+Custom.h" 7 8@interface SevenZipArchive (PrivateAPI) 9- (NSData *)dataByRunningSevenZip; 10- (NSArray *)listSevenZipContents:(NSArray *)lines; 11@end 12 13@implementation SevenZipArchive : Archive 14 15/** 16 * register our supported file extensions with superclass. 17 */ 18+ (void)initialize 19{ 20 [self registerFileExtension:@"7z" forArchiveClass:self]; 21 [self registerFileExtension:@"7za" forArchiveClass:self]; 22} 23 24+ (NSString *)archiveExecutable 25{ 26 return [Preferences sevenZipExecutable]; 27} 28+ (NSString *)unarchiveExecutable 29{ 30 return [Preferences sevenZipExecutable]; 31} 32 33+ (BOOL)hasRatio; 34{ 35 return NO; 36} 37 38+ (ArchiveType)archiveType 39{ 40 return SEVENZIP; 41} 42 43//------------------------------------------------------------------------------ 44// expanding the archive 45//------------------------------------------------------------------------------ 46- (int)expandFiles:(NSArray *)files withPathInfo:(BOOL)usePathInfo toPath:(NSString *)path 47{ 48 FileInfo *fileInfo; 49 NSMutableArray *args; 50 51 args = [NSMutableArray array]; 52 53 if (usePathInfo == YES) 54 { 55 [args addObject:@"x"]; 56 } 57 else 58 { 59 // extract flat 60 [args addObject:@"e"]; 61 } 62 63 // assume yes on all queries (needed for silently overwriting files) 64 [args addObject:@"-y"]; 65 66 // destination dir, path must not be separated with blank from the 'o' option 67 [args addObject:[@"-o" stringByAppendingString:path]]; 68 69 // protect for archives and files starting with - 70 [args addObject:@"--"]; 71 72 // add archive filename 73 [args addObject:[self path]]; 74 75 if (files != nil) 76 { 77 NSEnumerator *cursor = [files objectEnumerator]; 78 while ((fileInfo = [cursor nextObject]) != nil) 79 { 80 [args addObject:[fileInfo fullPath]]; 81 } 82 } 83 84 return [self runUnarchiverWithArguments:args]; 85} 86 87- (NSArray *)listContents 88{ 89 NSData *data = [self dataByRunningSevenZip]; 90 NSString *string = [[[NSString alloc] initWithData:data 91 encoding:NSASCIIStringEncoding] autorelease]; 92 NSArray *lines = [string componentsSeparatedByString:@"\n"]; 93 94 // take out first 8 lines (header) and last 2 lines (footer) 95 lines = [lines subarrayWithRange:NSMakeRange(17, [lines count] - 17)]; 96 lines = [lines subarrayWithRange:NSMakeRange(0, [lines count] - 3)]; 97 98 return [self listSevenZipContents:lines]; 99} 100 101//------------------------------------------------------------------------------ 102// creating archives 103//------------------------------------------------------------------------------ 104+ (void)createArchive:(NSString *)archivePath withFiles:(NSArray *)filenames archiveType: (ArchiveType) archiveType 105{ 106 NSEnumerator *filenameCursor; 107 NSString *filename; 108 NSString *workdir; 109 NSMutableArray *arguments; 110 111 // make sure archivePath has the correct suffix 112 if ([archivePath hasSuffix:@".7z"] == NO) 113 { 114 archivePath = [archivePath stringByAppendingString:@".7z"]; 115 } 116 // build arguments for commandline: 7z a filename <list of files> 117 arguments = [NSMutableArray array]; 118 [arguments addObject:@"a"]; 119 [arguments addObject:archivePath]; 120 121 // filenames contains absolute paths, convert them to relative paths. This works 122 // because you can select only files/directories below a current directory in 123 // GWorkspace so all the files *have* to have a common filesystem root. 124 filenameCursor = [filenames objectEnumerator]; 125 while ((filename = [filenameCursor nextObject]) != nil) 126 { 127 [arguments addObject:[filename lastPathComponent]]; 128 } 129 130 // change into this directory when running the task 131 workdir = [[filenames objectAtIndex:0] stringByDeletingLastPathComponent]; 132 133 [self runArchiverWithArguments:arguments inDirectory:workdir]; 134} 135 136//------------------------------------------------------------------------------ 137// private API 138//------------------------------------------------------------------------------ 139- (NSArray *)listSevenZipContents:(NSArray *)lines 140{ 141 NSEnumerator *cursor; 142 NSString *line; 143 NSMutableArray *results = [NSMutableArray array]; 144 145 cursor = [lines objectEnumerator]; 146 while ((line = [cursor nextObject]) != nil) 147 { 148 int length; 149 NSString *path, *date, *time, *attributes; 150 NSCalendarDate *calendarDate; 151 FileInfo *info; 152 NSArray *components; 153 154 if ([line length] == 0) 155 { 156 continue; 157 } 158 159 components = [line componentsSeparatedByString:@" "]; 160 components = [components arrayByRemovingEmptyStrings]; 161 162 // directly skip directory entries. Fortunately, 7zip displays attributes for each file 163 attributes = [components objectAtIndex:2]; 164 if ([attributes characterAtIndex:0] == 'D') 165 { 166 continue; 167 } 168 169 length = [[components objectAtIndex:3] intValue]; 170 171 // extract the path, it's always the last element 172 path = [[components lastObject] stringByRemovingWhitespaceFromBeginning]; 173 174 date = [components objectAtIndex:0]; 175 time = [components objectAtIndex:1]; 176 date = [NSString stringWithFormat:@"%@ %@", date, time]; 177 calendarDate = [NSCalendarDate dateWithString:date calendarFormat:@"%Y-%m-%d %H:%M:%S"]; 178 179 info = [FileInfo newWithPath:path date:calendarDate 180 size:[NSNumber numberWithInt:length]]; 181 [results addObject:info]; 182 } 183 return results; 184} 185 186- (NSData *)dataByRunningSevenZip 187{ 188 // l = list 189 NSArray *args = [NSArray arrayWithObjects:@"l", @"--", [self path], nil]; 190 return [self dataByRunningUnachiverWithArguments:args]; 191} 192 193@end 194