Index: utils/OFZIP.m ================================================================== --- utils/OFZIP.m +++ utils/OFZIP.m @@ -1,10 +1,11 @@ #import "OFApplication.h" #import "OFArray.h" #import "OFDictionary.h" #import "OFFile.h" #import "OFOptionsParser.h" +#import "OFSet.h" #import "OFStdIOStream.h" #import "OFZIPArchive.h" #import "OFZIPArchiveEntry.h" #import "autorelease.h" @@ -11,80 +12,89 @@ #import "macros.h" #define BUFFER_SIZE 4096 @interface OFZIP: OFObject -- (void)extractAllFilesFromArchive: (OFZIPArchive*)archive; +- (void)extractFiles: (OFArray*)files + fromArchive: (OFZIPArchive*)archive; @end OF_APPLICATION_DELEGATE(OFZIP) static void help(OFStream *stream, int status) { - [stream writeFormat: @"Usage: %@ -x archive1.zip [archive2.zip ...]\n", + [stream writeFormat: @"Usage: %@ -x archive1.zip [file1 file2 ...]\n", [OFApplication programName]]; [OFApplication terminateWithStatus: status]; } @implementation OFZIP - (void)applicationDidFinishLaunching { OFOptionsParser *optionsParser = [OFOptionsParser parserWithOptions: @"xh"]; - enum { - NONE, - EXTRACT - } mode; - OFEnumerator *enumerator; - OFString *file; - of_unichar_t option; + of_unichar_t option, mode = '\0'; + OFArray *remainingArguments; + void *pool; + OFZIPArchive *archive; + OFArray *files; while ((option = [optionsParser nextOption]) != '\0') { switch (option) { case 'x': - mode = EXTRACT; + if (mode != '\0') + help(of_stdout, 1); + + mode = option; break; case 'h': help(of_stdout, 0); break; - case '?': + default: [of_stderr writeFormat: @"%@: Unknown option: -%c\n", [OFApplication programName], [optionsParser lastOption]]; [OFApplication terminateWithStatus: 1]; - break; } } + remainingArguments = [optionsParser remainingArguments]; + switch (mode) { - case EXTRACT: - enumerator = - [[optionsParser remainingArguments] objectEnumerator]; - - while ((file = [enumerator nextObject]) != nil) { - void *pool = objc_autoreleasePoolPush(); - - [self extractAllFilesFromArchive: - [OFZIPArchive archiveWithPath: file]]; - - objc_autoreleasePoolPop(pool); - } + case 'x': + pool = objc_autoreleasePoolPush(); + + if ([remainingArguments count] < 1) + help(of_stderr, 1); + + files = [remainingArguments objectsInRange: + of_range(1, [remainingArguments count] - 1)]; + archive = [OFZIPArchive archiveWithPath: + [remainingArguments firstObject]]; + + [self extractFiles: files + fromArchive: archive]; + + objc_autoreleasePoolPop(pool); break; default: help(of_stderr, 1); break; } [OFApplication terminate]; } -- (void)extractAllFilesFromArchive: (OFZIPArchive*)archive +- (void)extractFiles: (OFArray*)files + fromArchive: (OFZIPArchive*)archive { OFEnumerator *enumerator = [[archive entries] objectEnumerator]; OFZIPArchiveEntry *entry; int_fast8_t override = 0; + bool all = ([files count] == 0); + OFMutableSet *missing = [OFMutableSet setWithArray: files]; while ((entry = [enumerator nextObject]) != nil) { void *pool = objc_autoreleasePoolPush(); OFString *fileName = [entry fileName]; OFString *outFileName = [fileName stringByStandardizingPath]; @@ -93,10 +103,15 @@ OFStream *stream; OFFile *output; char buffer[BUFFER_SIZE]; off_t written = 0, size = [entry uncompressedSize]; int_fast8_t percent = -1, newPercent; + + if (!all && ![files containsObject: fileName]) + continue; + + [missing removeObject: fileName]; #ifndef _WIN32 if ([outFileName hasPrefix: @"/"]) { #else if ([outFileName hasPrefix: @"/"] || @@ -187,7 +202,18 @@ [of_stdout writeFormat: @"\rExtracting %@... done\n", fileName]; objc_autoreleasePoolPop(pool); } + + if ([missing count] > 0) { + OFString *file; + + enumerator = [missing objectEnumerator]; + while ((file = [enumerator nextObject]) != nil) + [of_stderr writeFormat: + @"File %@ is not in the archive!\n", file]; + + [OFApplication terminateWithStatus: 1]; + } } @end