1/* 2 3 ZipArchive.m 4 Zipper 5 6 Copyright (C) 2012 Free Software Foundation, Inc 7 8 Authors: Dirk Olmes <dirk@xanthippe.ping.de> 9 Riccardo Mottola <rm@gnu.org> 10 11 This application is free software; you can redistribute it and/or modify it 12 under the terms of the GNU General Public License as published by the Free 13 Software Foundation; either version 2 of the License, or (at your option) 14 any later version. 15 16 This program is distributed in the hope that it will be useful, but 17 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 18 or FITNESS FOR A PARTICULAR PURPOSE. 19 See the GNU General Public License for more details 20 21 */ 22 23#import <Foundation/Foundation.h> 24#import "ZipArchive.h" 25#import "FileInfo.h" 26#import "NSString+Custom.h" 27#import "Preferences.h" 28#import "NSArray+Custom.h" 29 30// if the output contains this string in the first line, we have to use a different 31// parsing routine 32#define MINI_UNZIP_IDENTIFIER @"MiniUnz" 33 34static NSData *_magicBytes = nil; 35 36@interface ZipArchive (PrivateAPI) 37- (NSData *)dataByRunningUnzip; 38- (NSArray *)listUnzipContents:(NSArray *)lines; 39@end 40 41@implementation ZipArchive : Archive 42 43/** 44 * register our supported file extensions with superclass. 45 */ 46+ (void)initialize 47{ 48 // zip files start with 'P K 0x003 0x004' 49 char zipBytes[] = { 'P', 'K', 0x003, 0x004 }; 50 _magicBytes = [[NSData dataWithBytes:zipBytes length:4] retain]; 51 52 [self registerFileExtension:@"zip" forArchiveClass:self]; 53 [self registerFileExtension:@"jar" forArchiveClass:self]; 54} 55 56+ (NSString *)archiveExecutable 57{ 58 return [Preferences zipExecutable]; 59} 60+ (NSString *)unarchiveExecutable 61{ 62 return [Preferences unzipExecutable]; 63} 64 65 66+ (BOOL)hasRatio; 67{ 68 // unzip does provide info about the compression ratio 69 return YES; 70} 71 72+ (ArchiveType)archiveType 73{ 74 return ZIP; 75} 76 77+ (NSData *)magicBytes 78{ 79 return _magicBytes; 80} 81 82//------------------------------------------------------------------------------ 83// expanding the archive 84//------------------------------------------------------------------------------ 85- (int)expandFiles:(NSArray *)files withPathInfo:(BOOL)usePathInfo toPath:(NSString *)path 86{ 87 FileInfo *fileInfo; 88 NSMutableArray *args; 89 90 args = [NSMutableArray array]; 91 // be really quiet 92 [args addObject:@"-qq"]; 93 // overwrite without warning 94 [args addObject:@"-o"]; 95 if (usePathInfo == NO) 96 { 97 // junk paths 98 [args addObject:@"-j"]; 99 } 100 101 // destination dir 102 [args addObject:@"-d"]; 103 [args addObject:path]; 104 105 // protect against archives and files starting with - 106 [args addObject:@"--"]; 107 108 [args addObject:[self path]]; 109 110 if (files != nil) 111 { 112 NSEnumerator *cursor = [files objectEnumerator]; 113 while ((fileInfo = [cursor nextObject]) != nil) 114 { 115 [args addObject:[fileInfo fullPath]]; 116 } 117 } 118 119 return [self runUnarchiverWithArguments:args]; 120} 121 122- (NSArray *)listContents 123{ 124 NSData *data = [self dataByRunningUnzip]; 125 NSString *string = [[[NSString alloc] initWithData:data 126 encoding:NSASCIIStringEncoding] autorelease]; 127 NSArray *lines = [string componentsSeparatedByString:@"\n"]; 128 129 if ([[lines objectAtIndex:0] containsString:MINI_UNZIP_IDENTIFIER]) 130 { 131 // take out the first 6 lines (header) 132 lines = [lines subarrayWithRange:NSMakeRange(6, [lines count] - 6)]; 133 } 134 135 return [self listUnzipContents:lines]; 136} 137 138- (NSArray *)listUnzipContents:(NSArray *)lines 139{ 140 NSEnumerator *cursor; 141 NSString *line; 142 NSMutableArray *results = [NSMutableArray array]; 143 144 cursor = [lines objectEnumerator]; 145 while ((line = [cursor nextObject]) != nil) 146 { 147 int length, index; 148 NSString *path, *date, *time, *ratio, *checksum; 149 NSCalendarDate *calendarDate; 150 NSArray *components; 151 152 if (line == nil || [line length] == 0) 153 continue; 154 155 components = [line componentsSeparatedByString:@" "]; 156 components = [components arrayByRemovingEmptyStrings]; 157 158 length = [[components objectAtIndex:0] intValue]; 159 ratio = [components objectAtIndex:3]; 160 161 // extract the path. The checksum is the last token before the full path 162 // (which can contain blanks) 163 checksum = [components objectAtIndex:6]; 164 index = [line rangeOfString:checksum].location; 165 index += [checksum length]; 166 path = [[line substringFromIndex:index] stringByRemovingWhitespaceFromBeginning]; 167 168 date = [components objectAtIndex:4]; 169 time = [components objectAtIndex:5]; 170 date = [NSString stringWithFormat:@"%@ %@", date, time]; 171 calendarDate = [NSCalendarDate dateWithString:date calendarFormat:@"%m-%d-%Y %H:%M"]; 172 173 // we skip plain directory entries 174 if ([path hasSuffix:@"/"] == NO) 175 { 176 FileInfo *info; 177 178 info = [FileInfo newWithPath:path date:calendarDate 179 size:[NSNumber numberWithInt:length] ratio:ratio]; 180 if (info) 181 [results addObject:info]; 182 [info release]; 183 } 184 } 185 return results; 186} 187 188//------------------------------------------------------------------------------ 189// creating archives 190//------------------------------------------------------------------------------ 191+ (void)createArchive:(NSString *)archivePath withFiles:(NSArray *)filenames archiveType: (ArchiveType) archiveType 192{ 193 NSEnumerator *filenameCursor; 194 NSString *filename; 195 NSString *workdir; 196 NSMutableArray *arguments; 197 198 // make sure archivePath has the correct suffix 199 if ([archivePath hasSuffix:@".zip"] == NO) 200 { 201 archivePath = [archivePath stringByAppendingString:@".zip"]; 202 } 203 // build arguments for commandline: zip -r filename <list of files> 204 arguments = [NSMutableArray array]; 205 [arguments addObject:@"-r"]; 206 [arguments addObject:archivePath]; 207 208 // filenames contains absolute paths, convert them to relative paths. This works 209 // because you can select only files/directories below a current directory in 210 // GWorkspace so all the files *have* to have a common filesystem root. 211 filenameCursor = [filenames objectEnumerator]; 212 while ((filename = [filenameCursor nextObject]) != nil) 213 { 214 [arguments addObject:[filename lastPathComponent]]; 215 } 216 217 // change into this directory when running the task 218 workdir = [[filenames objectAtIndex:0] stringByDeletingLastPathComponent]; 219 220 [self runArchiverWithArguments:arguments inDirectory:workdir]; 221} 222 223//------------------------------------------------------------------------------ 224// private API 225//------------------------------------------------------------------------------ 226- (NSData *)dataByRunningUnzip 227{ 228 // l = list 229 // v = display all zip infos (Ratio etc.) 230 // qq = quiet, this is important for skipping comments in archives and for skipping 231 // the nice headers for readable output 232 NSArray *args = [NSArray arrayWithObjects:@"-lvqq", @"--", [self path], nil]; 233 return [self dataByRunningUnachiverWithArguments:args]; 234} 235 236@end 237