@@ -20,10 +20,11 @@ #import "OFApplication.h" #import "OFArray.h" #import "OFFile.h" #import "OFFileManager.h" #import "OFIRI.h" +#import "OFIRIHandler.h" #import "OFLocale.h" #import "OFOptionsParser.h" #import "OFSandbox.h" #import "OFStdIOStream.h" @@ -64,10 +65,11 @@ @"directory\n" @" -E --encoding= The encoding used by the archive\n" @" (only tar, lha and zoo files)\n" @" -f --force Force / overwrite files\n" @" -h --help Show this help\n" + @" --iri Use an IRI to access the archive\n" @" -l --list List all files in the archive\n" @" -n --no-clobber Never overwrite files\n" @" -p --print Print one or more files from the " @"archive\n" @" -q --quiet Quiet mode (no output, except " @@ -98,10 +100,22 @@ @"longopt1", longOption1, @"shortopt2", shortOption2Str, @"longopt2", longOption2)]; [OFApplication terminateWithStatus: 1]; } + +static OFIRI * +argumentToIRI(OFString *argument, bool isIRI) +{ + if (isIRI) + return [OFIRI IRIWithString: argument]; + + if ([argument isEqual: @"-"]) + return nil; + + return [OFIRI fileIRIWithPath: argument]; +} static void mutuallyExclusiveError5(OFUnichar shortOption1, OFString *longOption1, OFUnichar shortOption2, OFString *longOption2, OFUnichar shortOption3, OFString *longOption3, @@ -169,17 +183,19 @@ @implementation OFArc - (void)applicationDidFinishLaunching: (OFNotification *)notification { OFString *outputDir, *encodingString, *type; + bool isIRI; const OFOptionsParserOption options[] = { { 'a', @"append", 0, NULL, NULL }, { 'c', @"create", 0, NULL, NULL }, { 'C', @"directory", 1, NULL, &outputDir }, { 'E', @"encoding", 1, NULL, &encodingString }, { 'f', @"force", 0, NULL, NULL }, { 'h', @"help", 0, NULL, NULL }, + { 0, @"iri", 0, &isIRI, NULL }, { 'l', @"list", 0, NULL, NULL }, { 'n', @"no-clobber", 0, NULL, NULL }, { 'p', @"print", 0, NULL, NULL }, { 'q', @"quiet", 0, NULL, NULL }, { 't', @"type", 1, NULL, &type }, @@ -189,10 +205,11 @@ }; OFUnichar option, mode = '\0'; OFStringEncoding encoding = OFStringEncodingAutodetect; OFOptionsParser *optionsParser; OFArray OF_GENERIC(OFString *) *remainingArguments, *files; + OFIRI *IRI; id archive; #ifdef OF_HAVE_SANDBOX OFSandbox *sandbox = [OFSandbox sandbox]; sandbox.allowsStdIO = true; @@ -337,88 +354,90 @@ case 'a': case 'c': if (remainingArguments.count < 1) help(OFStdErr, false, 1); + IRI = argumentToIRI(remainingArguments.firstObject, isIRI); files = [remainingArguments objectsInRange: OFMakeRange(1, remainingArguments.count - 1)]; #ifdef OF_HAVE_SANDBOX - if (![remainingArguments.firstObject isEqual: @"-"]) - [sandbox unveilPath: remainingArguments.firstObject + if ([IRI.scheme isEqual: @"file"]) + [sandbox unveilPath: IRI.path permissions: (mode == 'a' ? @"rwc" : @"wc")]; for (OFString *path in files) [sandbox unveilPath: path permissions: @"r"]; sandbox.allowsUnveil = false; [OFApplication of_activateSandbox: sandbox]; #endif - archive = [self - openArchiveWithPath: remainingArguments.firstObject - type: type - mode: mode - encoding: encoding]; + archive = [self openArchiveWithIRI: IRI + type: type + mode: mode + encoding: encoding]; addFiles(archive, files); break; case 'l': if (remainingArguments.count != 1) help(OFStdErr, false, 1); + IRI = argumentToIRI(remainingArguments.firstObject, isIRI); + #ifdef OF_HAVE_SANDBOX - if (![remainingArguments.firstObject isEqual: @"-"]) - [sandbox unveilPath: remainingArguments.firstObject + if ([IRI.scheme isEqual: @"file"]) + [sandbox unveilPath: IRI.path permissions: @"r"]; sandbox.allowsUnveil = false; [OFApplication of_activateSandbox: sandbox]; #endif - archive = [self - openArchiveWithPath: remainingArguments.firstObject - type: type - mode: mode - encoding: encoding]; + archive = [self openArchiveWithIRI: IRI + type: type + mode: mode + encoding: encoding]; [archive listFiles]; break; case 'p': if (remainingArguments.count < 1) help(OFStdErr, false, 1); + IRI = argumentToIRI(remainingArguments.firstObject, isIRI); + files = [remainingArguments objectsInRange: + OFMakeRange(1, remainingArguments.count - 1)]; + #ifdef OF_HAVE_SANDBOX - if (![remainingArguments.firstObject isEqual: @"-"]) - [sandbox unveilPath: remainingArguments.firstObject + if ([IRI.scheme isEqual: @"file"]) + [sandbox unveilPath: IRI.path permissions: @"r"]; sandbox.allowsUnveil = false; [OFApplication of_activateSandbox: sandbox]; #endif - files = [remainingArguments objectsInRange: - OFMakeRange(1, remainingArguments.count - 1)]; - - archive = [self - openArchiveWithPath: remainingArguments.firstObject - type: type - mode: mode - encoding: encoding]; + archive = [self openArchiveWithIRI: IRI + type: type + mode: mode + encoding: encoding]; [archive printFiles: files]; break; case 'x': if (remainingArguments.count < 1) help(OFStdErr, false, 1); + IRI = argumentToIRI(remainingArguments.firstObject, isIRI); files = [remainingArguments objectsInRange: OFMakeRange(1, remainingArguments.count - 1)]; #ifdef OF_HAVE_SANDBOX - if (![remainingArguments.firstObject isEqual: @"-"]) - [sandbox unveilPath: remainingArguments.firstObject + if ([IRI.scheme isEqual: @"file"]) + [sandbox unveilPath: IRI.path permissions: @"r"]; if (files.count > 0) for (OFString *path in files) [sandbox unveilPath: path permissions: @"wc"]; @@ -435,15 +454,14 @@ sandbox.allowsUnveil = false; [OFApplication of_activateSandbox: sandbox]; #endif - archive = [self - openArchiveWithPath: remainingArguments.firstObject - type: type - mode: mode - encoding: encoding]; + archive = [self openArchiveWithIRI: IRI + type: type + mode: mode + encoding: encoding]; if (outputDir != nil) { OFFileManager *fileManager = [OFFileManager defaultManager]; @@ -455,30 +473,24 @@ } @try { [archive extractFiles: files]; } @catch (OFCreateDirectoryFailedException *e) { - OFString *error = [OFString - stringWithCString: strerror(e.errNo) - encoding: [OFLocale encoding]]; [OFStdErr writeString: @"\r"]; [OFStdErr writeLine: OF_LOCALIZED( @"failed_to_create_directory", @"Failed to create directory %[dir]: %[error]", @"dir", e.IRI.fileSystemRepresentation, - @"error", error)]; + @"error", OFStrError(e.errNo))]; _exitStatus = 1; } @catch (OFOpenItemFailedException *e) { - OFString *error = [OFString - stringWithCString: strerror(e.errNo) - encoding: [OFLocale encoding]]; [OFStdErr writeString: @"\r"]; [OFStdErr writeLine: OF_LOCALIZED( @"failed_to_open_file", @"Failed to open file %[file]: %[error]", @"file", e.path, - @"error", error)]; + @"error", OFStrError(e.errNo))]; _exitStatus = 1; } break; default: @@ -487,27 +499,20 @@ } [OFApplication terminateWithStatus: _exitStatus]; } -- (id )openArchiveWithPath: (OFString *)path_ - type: (OFString *)type - mode: (char)mode - encoding: (OFStringEncoding)encoding +- (id )openArchiveWithIRI: (OFIRI *)IRI + type: (OFString *)type + mode: (char)mode + encoding: (OFStringEncoding)encoding { /* To make clang-analyzer happy about assigning nil to path later. */ - OFString *path = path_; OFString *modeString, *fileModeString; OFStream *file = nil; id archive = nil; - [_archivePath release]; - _archivePath = [path copy]; - - if (path == nil) - return nil; - switch (mode) { case 'a': modeString = @"a"; fileModeString = @"r+"; break; @@ -522,11 +527,11 @@ break; default: @throw [OFInvalidArgumentException exception]; } - if ([path isEqual: @"-"]) { + if (IRI == nil) { switch (mode) { case 'a': case 'c': file = OFStdOut; break; @@ -536,31 +541,27 @@ file = OFStdIn; break; default: @throw [OFInvalidArgumentException exception]; } - - path = nil; } else { @try { - file = [OFFile fileWithPath: path mode: fileModeString]; + file = [OFIRIHandler openItemAtIRI: IRI + mode: fileModeString]; } @catch (OFOpenItemFailedException *e) { - OFString *error = [OFString - stringWithCString: strerror(e.errNo) - encoding: [OFLocale encoding]]; [OFStdErr writeString: @"\r"]; [OFStdErr writeLine: OF_LOCALIZED( @"failed_to_open_file", @"Failed to open file %[file]: %[error]", - @"file", e.path, - @"error", error)]; + @"file", e.IRI.string, + @"error", OFStrError(e.errNo))]; [OFApplication terminateWithStatus: 1]; } } if (type == nil || [type isEqual: @"auto"]) { - OFString *lowercasePath = path.lowercaseString; + OFString *lowercasePath = IRI.path.lowercaseString; /* This one has to be first for obvious reasons */ if ([lowercasePath hasSuffix: @".tar.gz"] || [lowercasePath hasSuffix: @".tgz"]) type = @"tgz"; @@ -579,42 +580,42 @@ type = @"zip"; } @try { if ([type isEqual: @"gz"]) - archive = [GZIPArchive archiveWithPath: path - stream: file - mode: modeString - encoding: encoding]; - else if ([type isEqual: @"lha"]) - archive = [LHAArchive archiveWithPath: path - stream: file - mode: modeString - encoding: encoding]; - else if ([type isEqual: @"tar"]) - archive = [TarArchive archiveWithPath: path + archive = [GZIPArchive archiveWithIRI: IRI stream: file mode: modeString encoding: encoding]; + else if ([type isEqual: @"lha"]) + archive = [LHAArchive archiveWithIRI: IRI + stream: file + mode: modeString + encoding: encoding]; + else if ([type isEqual: @"tar"]) + archive = [TarArchive archiveWithIRI: IRI + stream: file + mode: modeString + encoding: encoding]; else if ([type isEqual: @"tgz"]) { OFStream *GZIPStream = [OFGZIPStream streamWithStream: file mode: modeString]; - archive = [TarArchive archiveWithPath: path - stream: GZIPStream - mode: modeString - encoding: encoding]; + archive = [TarArchive archiveWithIRI: IRI + stream: GZIPStream + mode: modeString + encoding: encoding]; } else if ([type isEqual: @"zip"]) - archive = [ZIPArchive archiveWithPath: path - stream: file - mode: modeString - encoding: encoding]; + archive = [ZIPArchive archiveWithIRI: IRI + stream: file + mode: modeString + encoding: encoding]; else if ([type isEqual: @"zoo"]) - archive = [ZooArchive archiveWithPath: path - stream: file - mode: modeString - encoding: encoding]; + archive = [ZooArchive archiveWithIRI: IRI + stream: file + mode: modeString + encoding: encoding]; else { [OFStdErr writeLine: OF_LOCALIZED( @"unknown_archive_type", @"Unknown archive type: %[type]", @"type", type)]; @@ -627,32 +628,26 @@ goto error; } @throw e; } @catch (OFReadFailedException *e) { - OFString *error = [OFString - stringWithCString: strerror(e.errNo) - encoding: [OFLocale encoding]]; [OFStdErr writeLine: OF_LOCALIZED(@"failed_to_read_file", @"Failed to read file %[file]: %[error]", - @"file", path, - @"error", error)]; + @"file", IRI.string, + @"error", OFStrError(e.errNo))]; goto error; } @catch (OFSeekFailedException *e) { - OFString *error = [OFString - stringWithCString: strerror(e.errNo) - encoding: [OFLocale encoding]]; [OFStdErr writeLine: OF_LOCALIZED(@"failed_to_seek_in_file", @"Failed to seek in file %[file]: %[error]", - @"file", path, - @"error", error)]; + @"file", IRI.string, + @"error", OFStrError(e.errNo))]; goto error; } @catch (OFInvalidFormatException *e) { [OFStdErr writeLine: OF_LOCALIZED( @"file_is_not_a_valid_archive", @"File %[file] is not a valid archive!", - @"file", path)]; + @"file", IRI.string)]; goto error; } if ((mode == 'a' || mode == 'c') && ![archive respondsToSelector: @selector(addFiles:)]) { @@ -661,12 +656,12 @@ } return archive; error: - if (mode == 'c' && path != nil) - [[OFFileManager defaultManager] removeItemAtPath: path]; + if (mode == 'c' && IRI != nil) + [[OFFileManager defaultManager] removeItemAtIRI: IRI]; [OFApplication terminateWithStatus: 1]; abort(); } @@ -737,32 +732,26 @@ size_t length; @try { length = [input readIntoBuffer: buffer length: bufferSize]; } @catch (OFReadFailedException *e) { - OFString *error = [OFString - stringWithCString: strerror(e.errNo) - encoding: [OFLocale encoding]]; [OFStdOut writeString: @"\r"]; [OFStdErr writeLine: OF_LOCALIZED(@"failed_to_read_file", @"Failed to read file %[file]: %[error]", @"file", fileName, - @"error", error)]; + @"error", OFStrError(e.errNo))]; return -1; } @try { [output writeBuffer: buffer length: length]; } @catch (OFWriteFailedException *e) { - OFString *error = [OFString - stringWithCString: strerror(e.errNo) - encoding: [OFLocale encoding]]; [OFStdOut writeString: @"\r"]; [OFStdErr writeLine: OF_LOCALIZED(@"failed_to_write_file", @"Failed to write file %[file]: %[error]", @"file", fileName, - @"error", error)]; + @"error", OFStrError(e.errNo))]; return -1; } return length; }