diff options
Diffstat (limited to 'Foundation')
-rw-r--r-- | Foundation/GTMScriptRunner.m | 86 | ||||
-rw-r--r-- | Foundation/GTMScriptRunnerTest.m | 7 |
2 files changed, 63 insertions, 30 deletions
diff --git a/Foundation/GTMScriptRunner.m b/Foundation/GTMScriptRunner.m index fce6915..85adae7 100644 --- a/Foundation/GTMScriptRunner.m +++ b/Foundation/GTMScriptRunner.m @@ -19,6 +19,8 @@ #import "GTMScriptRunner.h" #import "GTMDefines.h" +#include <sys/ioctl.h> + static BOOL LaunchNSTaskCatchingExceptions(NSTask *task); @interface GTMScriptRunner (PrivateMethods) @@ -93,46 +95,82 @@ static BOOL LaunchNSTaskCatchingExceptions(NSTask *task); NSTask *task = [self interpreterTaskWithAdditionalArgs:nil]; NSFileHandle *toTask = [[task standardInput] fileHandleForWriting]; NSFileHandle *fromTask = [[task standardOutput] fileHandleForReading]; - + NSFileHandle *errTask = [[task standardError] fileHandleForReading]; + if (!LaunchNSTaskCatchingExceptions(task)) { return nil; } [toTask writeData:[cmds dataUsingEncoding:NSUTF8StringEncoding]]; [toTask closeFile]; - - NSData *outData = [fromTask readDataToEndOfFile]; - NSString *output = [[[NSString alloc] initWithData:outData - encoding:NSUTF8StringEncoding] autorelease]; - - // Handle returning standard error if |err| is not nil - if (err) { - NSFileHandle *stderror = [[task standardError] fileHandleForReading]; - NSData *errData = [stderror readDataToEndOfFile]; - *err = [[[NSString alloc] initWithData:errData - encoding:NSUTF8StringEncoding] autorelease]; - if (trimsWhitespace_) { - *err = [*err stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - } - // let folks test for nil instead of @"" - if ([*err length] < 1) { - *err = nil; + // Must keep both file handle buffers empty to avoid the deadlock described in + // http://code.google.com/p/google-toolbox-for-mac/issues/detail?id=25 + NSMutableString *mutableOutString = [NSMutableString string]; + NSMutableString *mutableErrString = [NSMutableString string]; + while (true) { + // availableByteCountNonBlocking must be called on both fromTask and errTask + // each time through the loop. + unsigned int bytesFromTask = [self availableByteCountNonBlocking:fromTask]; + unsigned int bytesErrTask = [self availableByteCountNonBlocking:errTask]; + if (![task isRunning] && (bytesFromTask == 0) && (bytesErrTask == 0)) { + break; + } + if (bytesFromTask > 0) { + NSData *outData = [fromTask availableData]; + NSString *dataString = + [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding]; + [mutableOutString appendString:dataString]; + [dataString release]; + } + if (bytesErrTask > 0 && err) { + NSData *errData = [errTask availableData]; + NSString *dataString = + [[NSString alloc] initWithData:errData encoding:NSUTF8StringEncoding]; + [mutableErrString appendString:dataString]; + [dataString release]; } } [task terminate]; - + + NSString *outString = mutableOutString; + NSString *errString = mutableErrString; + if (trimsWhitespace_) { - output = [output stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + outString = [outString stringByTrimmingCharactersInSet:set]; + if (err) { + errString = [errString stringByTrimmingCharactersInSet:set]; + } } // let folks test for nil instead of @"" - if ([output length] < 1) { - output = nil; + if ([outString length] < 1) { + outString = nil; } - - return output; + + // Handle returning standard error if |err| is not nil + if (err) { + // let folks test for nil instead of @"" + if ([errString length] < 1) { + *err = nil; + } else { + *err = errString; + } + } + + return outString; +} + +- (unsigned int)availableByteCountNonBlocking:(NSFileHandle *)file { + int fd = [file fileDescriptor]; + int numBytes; + if (ioctl(fd, FIONREAD, (char *) &numBytes) == -1) { + [NSException raise:NSFileHandleOperationException + format:@"ioctl() error %d", errno]; + } + return numBytes; } - (NSString *)runScript:(NSString *)path { diff --git a/Foundation/GTMScriptRunnerTest.m b/Foundation/GTMScriptRunnerTest.m index f13fe6c..07928c2 100644 --- a/Foundation/GTMScriptRunnerTest.m +++ b/Foundation/GTMScriptRunnerTest.m @@ -407,24 +407,19 @@ output = [sr run:cmd standardError:&err]; STAssertEquals([output length], (NSUInteger)(512 + 512*200), nil); STAssertEquals([err length], (NSUInteger)512, nil); -#if 0 - // Not fixed yet + cmd = [NSString stringWithFormat:GENERATOR_FORMAT_STR, @"'b1', 'e200'"]; STAssertNotNil(cmd, nil); output = [sr run:cmd standardError:&err]; STAssertEquals([output length], (NSUInteger)512, nil); STAssertEquals([err length], (NSUInteger)(512 + 512*200), nil); -#endif // Now send a large amount down both to make sure we spool it all in. -#if 0 - // Not fixed yet cmd = [NSString stringWithFormat:GENERATOR_FORMAT_STR, @"'b200'"]; STAssertNotNil(cmd, nil); output = [sr run:cmd standardError:&err]; STAssertEquals([output length], (NSUInteger)(512*200), nil); STAssertEquals([err length], (NSUInteger)(512*200), nil); -#endif } |