Re: NSTask with unzip
Re: NSTask with unzip
- Subject: Re: NSTask with unzip
- From: Leonardo <email@hidden>
- Date: Sun, 28 Nov 2010 23:51:17 +0100
- Thread-topic: NSTask with unzip
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