Index: utils/ofzip/Archive.h ================================================================== --- utils/ofzip/Archive.h +++ utils/ofzip/Archive.h @@ -21,6 +21,7 @@ @protocol Archive + (instancetype)archiveWithFile: (OFFile*)file; - initWithFile: (OFFile*)file; - (void)listFiles; - (void)extractFiles: (OFArray OF_GENERIC(OFString*)*)files; +- (void)printFiles: (OFArray OF_GENERIC(OFString*)*)files; @end Index: utils/ofzip/GZIPArchive.m ================================================================== --- utils/ofzip/GZIPArchive.m +++ utils/ofzip/GZIPArchive.m @@ -115,6 +115,30 @@ } if (app->_outputLevel >= 0) [of_stdout writeFormat: @"\rExtracting %@... done\n", fileName]; } + +- (void)printFiles: (OFArray OF_GENERIC(OFString*)*)files +{ + OFString *fileName = [[app->_archivePath lastPathComponent] + stringByDeletingPathExtension]; + + if ([files count] > 0) { + [of_stderr writeLine: + @"Cannot specify a file to print for .gz archives!"]; + app->_exitStatus = 1; + return; + } + + while (![_stream isAtEndOfStream]) { + ssize_t length = [app copyBlockFromStream: _stream + toStream: of_stdout + fileName: fileName]; + + if (length < 0) { + app->_exitStatus = 1; + return; + } + } +} @end Index: utils/ofzip/OFZIP.m ================================================================== --- utils/ofzip/OFZIP.m +++ utils/ofzip/OFZIP.m @@ -41,20 +41,22 @@ static void help(OFStream *stream, bool full, int status) { [stream writeFormat: - @"Usage: %@ -[fhlnqvx] archive.zip [file1 file2 ...]\n", + @"Usage: %@ -[fhlnpqvx] archive.zip [file1 file2 ...]\n", [OFApplication programName]]; if (full) [stream writeString: @"\nOptions:\n" @" -f --force Force / overwrite files\n" @" -h --help Show this help\n" @" -l --list List all files in the archive\n" @" -n --no-clober Never overwrite files\n" + @" -p --print Print one or more files from the " + @"archive\n" @" -q --quiet Quiet mode (no output, except " @"errors)\n" @" -v --verbose Verbose output for file list\n" @" -x --extract Extract files\n"]; @@ -68,19 +70,32 @@ [of_stderr writeFormat: @"Error: -%C / --%@ and -%C / --%@ are mutually exclusive!\n", shortOption1, longOption1, shortOption2, longOption2]; [OFApplication terminateWithStatus: 1]; } + +static void +mutuallyExclusiveError3(of_unichar_t shortOption1, OFString *longOption1, + of_unichar_t shortOption2, OFString *longOption2, + of_unichar_t shortOption3, OFString *longOption3) +{ + [of_stderr writeFormat: + @"Error: -%C / --%@, -%C / --%@ and -%C / --%@ are mutually " + @"exclusive!\n", shortOption1, longOption1, shortOption2, + longOption2, shortOption3, longOption3]; + [OFApplication terminateWithStatus: 1]; +} @implementation OFZIP - (void)applicationDidFinishLaunching { const of_options_parser_option_t options[] = { { 'f', @"force", 0, NULL, NULL }, { 'h', @"help", 0, NULL, NULL }, { 'l', @"list", 0, NULL, NULL }, { 'n', @"no-clobber", 0, NULL, NULL }, + { 'p', @"print", 0, NULL, NULL }, { 'q', @"quiet", 0, NULL, NULL }, { 'v', @"verbose", 0, NULL, NULL }, { 'x', @"extract", 0, NULL, NULL }, { '\0', nil, 0, NULL, NULL } }; @@ -120,13 +135,15 @@ _outputLevel--; break; case 'l': case 'x': + case 'p': if (mode != '\0') - mutuallyExclusiveError( - 'l', @"list", 'x', @"extract"); + mutuallyExclusiveError3( + 'l', @"list", 'x', @"extract", + 'p', @"print"); mode = option; break; case 'h': help(of_stdout, true, 0); @@ -185,10 +202,19 @@ [e path], strerror([e errNo])]; _exitStatus = 1; } break; + case 'p': + if ([remainingArguments count] < 1) + help(of_stderr, false, 1); + + files = [remainingArguments objectsInRange: + of_range(1, [remainingArguments count] - 1)]; + + [archive printFiles: files]; + break; default: help(of_stderr, true, 1); break; } Index: utils/ofzip/ZIPArchive.m ================================================================== --- utils/ofzip/ZIPArchive.m +++ utils/ofzip/ZIPArchive.m @@ -14,10 +14,12 @@ * file. */ #include "config.h" +#include + #import "OFDate.h" #import "OFSet.h" #import "OFApplication.h" #import "OFFileManager.h" #import "OFStdIOStream.h" @@ -24,10 +26,11 @@ #import "ZIPArchive.h" #import "OFZIP.h" #import "OFInvalidFormatException.h" +#import "OFOpenItemFailedException.h" #ifndef S_IRWXG # define S_IRWXG 0 #endif #ifndef S_IRWXO @@ -256,6 +259,46 @@ @"File %@ is not in the archive!\n", file]; app->_exitStatus = 1; } } + +- (void)printFiles: (OFArray OF_GENERIC(OFString*)*)files +{ + OFStream *stream; + + if ([files count] < 1) { + [of_stderr writeLine: @"Need one or more files to print!"]; + app->_exitStatus = 1; + return; + } + + for (OFString *path in files) { + @try { + stream = [_archive streamForReadingFile: path]; + } @catch (OFOpenItemFailedException *e) { + if ([e errNo] == ENOENT) { + [of_stderr writeFormat: + @"File %@ is not in the archive!\n", + [e path]]; + app->_exitStatus = 1; + continue; + } + + @throw e; + } + + while (![stream isAtEndOfStream]) { + ssize_t length = [app copyBlockFromStream: stream + toStream: of_stdout + fileName: path]; + + if (length < 0) { + app->_exitStatus = 1; + return; + } + } + + [stream close]; + } +} @end