Re: NSTask with unzip
Re: NSTask with unzip
- Subject: Re: NSTask with unzip
- From: Tim Schröder <email@hidden>
- Date: Tue, 30 Nov 2010 22:29:38 +0100
There are several free zlib frameworks for Cocoa available, have a look at http://www.timschroeder.net/2010/07/25/the-seven-cities-of-zip/ for a comparison.
--
Tim Schröder
Am 28.11.2010 um 23:51 schrieb Leonardo:
> Great! Thanks for the advises.
> Now I have to zip and unzip a NSData (not a zip file) to a NSData.
> In other words, I have to zip and unzip "data to data", without using any
> file. Some idea?
>
> -- Leonardo
>
>
>
>> Da: Ben Haller <email@hidden>
>> Data: Sun, 28 Nov 2010 08:52:33 -0500
>> A: Leonardo <email@hidden>, Cocoa List <email@hidden>
>> Oggetto: Re: NSTask with unzip
>>
>> Yes, this looks good. I like your category on NSFileHandle (not a
>> subclass!); it's cleaner than the code at the link I sent you, since it
>> doesn't just eat the error, and it's better as a category.
>>
>> Four things I would mention:
>>
>> 1) Checking that the pipe could be created and actually has a file handle for
>> reading would be a good idea; [NSPipe pipe] is documented as being allowed to
>> return nil
>>
>> 2) Checking -terminationStatus is a good idea once the task completes (after
>> you're done pulling data out, you can then safely call -waitUntilExit on the
>> task to be certain it has completed before calling -terminationStatus, AFAIK)
>>
>> 3) You only use a pipe for standard out, not for standard in, but it's worth
>> noting that a pipe for standard in needs to receive a -closeFile call or the
>> file descriptor for that pipe doesn't get deleted correctly. As a reminder to
>> myself about this issue, I just send -closeFile to all of the pipes I'm using
>> with a task, so that I don't forget to do it. But your code is correct; I
>> mention this just in case someone reading the archives adapts this code to a
>> task that requires a standard in pipe.
>>
>> 4) -launch can raise, so it is good to think about that; but as long as you're
>> comfortable with your method raising, your code seems fine to me.
>>
>> Good stuff! If anybody on the list knows whether the bug that the
>> -availableDataOrError: hack circumvents has been fixed, and in what OS X
>> release, I'd love to know that so I know whether it's safe to delete that
>> rather unpleasant hack from my code.
>>
>> Ben Haller
>> McGill University
>>
>>
>> On 2010-11-27, at 3:43 PM, Leonardo wrote:
>>
>>> Ben, thank you so much! I have successfully done it.
>>> I post the code here for anyone to use it. I love this list.
>>>
>>> - (NSData*)UnzipFile:(NSString*)sourcePath
>>> extractFileName:(NSString*)extractFileName
>>> {
>>> NSTask *unzip = [[[NSTask alloc] init] autorelease];
>>> NSPipe *aPipe = [NSPipe pipe];
>>> [unzip setStandardOutput:aPipe];
>>> [unzip setLaunchPath:@"/usr/bin/unzip"];
>>> [unzip setArguments:[NSArray arrayWithObjects:@"-p", sourcePath,
>>> extractFileName, nil]];
>>> [unzip launch];
>>>
>>> NSMutableData *dataOut = [NSMutableData data];
>>> NSData *dataIn = nil;
>>> NSException *error = nil;
>>>
>>> while((dataIn = [[aPipe fileHandleForReading]
>>> availableDataOrError:&error]) && [dataIn length] && error == nil){
>>> [dataOut appendData:dataIn];
>>> }
>>>
>>> if([dataOut length] && error == nil){
>>> return dataOut;
>>> }
>>>
>>> return nil;
>>> }
>>>
>>>
>>> // Then I subclassed NSFileHandler this way
>>>
>>> @implementation NSFileHandle (MyOwnAdditions)
>>> - (NSData*)availableDataOrError:(NSException**)returnError
>>> {
>>> for(;;){
>>> @try{
>>> return [self availableData];
>>> }@catch (NSException *e) {
>>> if ([[e name] isEqualToString:NSFileHandleOperationException]) {
>>> if ([[e reason] isEqualToString:@"*** -[NSConcreteFileHandle
>>> availableData]: Interrupted system call"]) {
>>> continue;
>>> }
>>> if (returnError)
>>> *returnError = e;
>>> return nil;
>>> }
>>> @throw;
>>> }
>>> }
>>> }
>>> @end
>>>
>>>
>>>> Da: Ben Haller <email@hidden>
>>>> Data: Sat, 27 Nov 2010 12:12:39 -0500
>>>> A: Dave DeLong <email@hidden>
>>>> Cc: "gMail.com" <email@hidden>, Cocoa List
>>>> <email@hidden>
>>>> Oggetto: Re: NSTask with unzip
>>>>
>>>> Here's a post that I found useful:
>>>>
>>>> http://dev.notoptimal.net/2007/04/nstasks-nspipes-and-deadlocks-when.html
>>>>
>>>> Dave, not sure what you mean here. NSPipe uses NSFileHandle. Does using
>>>> an
>>>> NSFileHandle directly change things somehow? If so, why? I think this is
>>>> an
>>>> avenue I haven't explored; once I (finally) figured out the right magic
>>>> incantations to get things to work reliably with NSPipe, I now recycle that
>>>> code everywhere I need an NSTask :->.
>>>>
>>>> Ben Haller
>>>> McGill University
>>>>
>>>>
>>>> On 2010-11-27, at 11:48 AM, Dave DeLong wrote:
>>>>
>>>>> The way I get around this is to use an NSFileHandle for standard out
>>>>> instead
>>>>> of an NSPipe. It's a bit less efficient, but slightly more convenient.
>>>>>
>>>>> Dave
>>>>>
>>>>> Sent from my iPhone
>>>>>
>>>>> On Nov 27, 2010, at 7:59 AM, Ben Haller <email@hidden>
>>>>> wrote:
>>>>>
>>>>>> On 2010-11-26, at 7:33 AM, gMail.com wrote:
>>>>>>
>>>>>>> Hi, I can properly unzip a zip file launching a NSTask with
>>>>>>> /usr/bin/unzip
>>>>>>> The task saves the unzipped file to the disk, then a I read the unzipped
>>>>>>> file in a NSData. Well. My question is:
>>>>>>> Can I do the same job without saving the unzipped file to the disk?
>>>>>>>
>>>>>>> I have tried to set the standard output to a pipe - which works well with
>>>>>>> other tasks - but here it doesn't work. The task never exits. Here's the
>>>>>>> wrong code:
>>>>>>>
>>>>>>> NSTask *unzip = [[[NSTask alloc] init] autorelease];
>>>>>>> [unzip setLaunchPath:@"/usr/bin/unzip"];
>>>>>>> [unzip setArguments:[NSArray arrayWithObjects:@"-p", zipfile,
>>>>>>> @"filetounzip", nil]];
>>>>>>>
>>>>>>> NSPipe *aPipe = [NSPipe pipe];
>>>>>>> [unzip setStandardOutput:aPipe];
>>>>>>> [unzip launch];
>>>>>>> [unzip waitUntilExit];
>>>>>>>
>>>>>>> if([unzip terminationStatus] == noErr){
>>>>>>> dictData = [NSMutableData data];
>>>>>>> while((dataOut = [aPipe availableData]) && [dataOut length]){
>>>>>>> [dictData appendData:dataOut];
>>>>>>> }
>>>>>>> }
>>>>>>
>>>>>> If I recall correctly, the problem is likely to be your use of
>>>>>> -waitUntilExit. That API should apparently have a large red label on it
>>>>>> ("Warnin', lark's vomit!") since everybody wants to use it this way. The
>>>>>> problem is that the task's output pipe fills up because it isn't being
>>>>>> serviced, and then things get locked up. You need to go with asynchronous
>>>>>> reads to service the pipe as output gets stuffed into it. There should be
>>>>>> lots of examples of this on this list, now that you know what to look for.
>>>>>>
>>>>>> What would be great would be a new call, along the lines of
>>>>>> -dataFromWaitingUntilExit or some such, that does all this for you, since
>>>>>> this is so commonly what people want to do.
>>>>>>
>>>>>> Ben Haller
>>>>>> McGill University
>>>>>>
>>>>>> _______________________________________________
>>>>>>
>>>>>> Cocoa-dev mailing list (email@hidden)
>>>>>>
>>>>>> Please do not post admin requests or moderator comments to the list.
>>>>>> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>>>>>>
>>>>>> Help/Unsubscribe/Update your Subscription:
>>>>>>
>>>>>> This email sent to email@hidden
>>>>
>>>
>>>
>>
>
>
> _______________________________________________
>
> Cocoa-dev mailing list (email@hidden)
>
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden