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