#import #import "TarArchive.h" #import "FileInfo.h" #import "NSString+Custom.h" #import "Preferences.h" #import "NSArray+Custom.h" @interface TarArchive (PrivateAPI) - (NSData *)dataByRunningTar; - (FileInfo *)fileInfoFromLine:(NSString *)line; @end @implementation TarArchive : Archive /** * register our supported file extensions with superclass. */ + (void)initialize { [self registerFileExtension:@"tar" forArchiveClass:self]; [self registerFileExtension:@"tar.gz" forArchiveClass:self]; [self registerFileExtension:@"tgz" forArchiveClass:self]; [self registerFileExtension:@"tar.bz2" forArchiveClass:self]; } + (NSString *)unarchiveExecutable { return [Preferences tarExecutable]; } /** * Tar files inherently have the full path info and can't be uncompressed flat. */ + (BOOL)canExtractWithoutFullPath; { return NO; } + (NSString *)archiveType { return @"tar"; } //------------------------------------------------------------------------------ // expanding the archive //------------------------------------------------------------------------------ - (int)expandFiles:(NSArray *)files withPathInfo:(BOOL)usePathInfo toPath:(NSString *)path { FileInfo *fileInfo; NSString *compressionArg; NSMutableArray *args; compressionArg = [Preferences compressionArgumentForFile:[self path]]; NSParameterAssert(compressionArg != nil); args = [NSMutableArray array]; [args addObject:@"-x"]; // compression method [args addObject:compressionArg]; // destination dir [args addObject:@"-C"]; [args addObject:path]; // the archive [args addObject:@"-f"]; [args addObject:[self path]]; if (files != nil) { NSEnumerator *cursor = [files objectEnumerator]; while ((fileInfo = [cursor nextObject]) != nil) { [args addObject:[fileInfo fullPath]]; } } return [self runUnarchiverWithArguments:args]; } - (NSArray *)listContents { NSString *line; NSMutableArray *results = [NSMutableArray array]; NSData *data = [self dataByRunningTar]; NSString *string = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]; NSArray *lines = [string componentsSeparatedByString:@"\n"]; NSEnumerator *cursor = [lines objectEnumerator]; while ((line = [cursor nextObject]) != nil) { FileInfo *info; // BSD tar seems to add linefeed at the end of the line. strip that if ([line hasSuffix:@"\r"]) { line = [line substringToIndex:[line length] - 1]; } // we skip empty lines and plain directory entries if (([line length] == 0) || [line hasSuffix:@"/"]) { continue; } info = [self fileInfoFromLine:line]; [results addObject:info]; } return results; } //------------------------------------------------------------------------------ // creating archives //------------------------------------------------------------------------------ + (void)createArchive:(NSString *)archivePath withFiles:(NSArray *)filenames { NSEnumerator *filenameCursor; NSString *filename; NSString *workdir; NSMutableArray *arguments; // make sure archivePath has the correct suffix if ([archivePath hasSuffix:@".tar.gz"] == NO) { archivePath = [archivePath stringByAppendingString:@".tar.gz"]; } // build arguments for commandline: tar -czf filename arguments = [NSMutableArray array]; [arguments addObject:@"-czf"]; [arguments addObject:archivePath]; // filenames contains absolute paths, convert them to relative paths. This works // because you can select only files/directories below a current directory in // GWorkspace so all the files *have* to have a common filesystem root. filenameCursor = [filenames objectEnumerator]; while ((filename = [filenameCursor nextObject]) != nil) { [arguments addObject:[filename lastPathComponent]]; } // change into this directory when running the task workdir = [[filenames objectAtIndex:0] stringByDeletingLastPathComponent]; // create the archive. In the case of TarArchive the unarchiver can also create // archives [self runUnarchiverWithArguments:arguments inDirectory:workdir]; } //------------------------------------------------------------------------------ // private API //------------------------------------------------------------------------------ - (NSData *)dataByRunningTar { NSString *compressionArg; NSMutableArray *arguments; compressionArg = [Preferences compressionArgumentForFile:[self path]]; NSParameterAssert(compressionArg != nil); arguments = [NSMutableArray arrayWithObject:@"-tv"]; if ([compressionArg isEqual:@""] == NO) { [arguments addObject:compressionArg]; } [arguments addObject:@"-f"]; [arguments addObject:[self path]]; return [self dataByRunningUnachiverWithArguments:arguments]; } - (FileInfo *)fileInfoFromLine:(NSString *)line { int index, length = -1; NSString *path = nil; NSString *dateString = nil; NSString *time = nil; NSCalendarDate *calendarDate = nil; NSArray *components = [line componentsSeparatedByString:@" "]; components = [components arrayByRemovingEmptyStrings]; if ([Preferences isBsdTar]) { NSArray *dateComponents; // BSD tar length = [[components objectAtIndex:2] intValue]; dateComponents = [components subarrayWithRange:NSMakeRange(3, 4)]; dateString = [dateComponents componentsJoinedByString:@" "]; calendarDate = [NSCalendarDate dateWithString:dateString calendarFormat:@"%b %d %H:%M %Y"]; } else { // linux tar NSString *date; length = [[components objectAtIndex:2] intValue]; date = [components objectAtIndex:3]; time = [components objectAtIndex:4]; dateString = [NSString stringWithFormat:@"%@ %@", date, time]; calendarDate = [NSCalendarDate dateWithString:date calendarFormat:@"%Y-%m-%d %H:%M:%S"]; } // The path is everything afer the date string. Since it can contain blanks, // do *not* just grab any objects from components array index = [line rangeOfString:dateString].location; index += [dateString length]; path = [[line substringFromIndex:index] stringByRemovingWhitespaceFromBeginning]; return [FileInfo newWithPath:path date:calendarDate size:[NSNumber numberWithInt:length]]; } @end