From f38d4ee0e9157d2ac3c9a08fde37b9e19457fb0f Mon Sep 17 00:00:00 2001 From: thomasvl Date: Wed, 27 Feb 2008 03:54:04 +0000 Subject: Found and fixed a bug in the regex enumerators that was causing them to incorrectly walk a string when using '^' in an expression. Added GTMScriptRunner for spawning scripts. --- Foundation/GTMScriptRunner.m | 246 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 Foundation/GTMScriptRunner.m (limited to 'Foundation/GTMScriptRunner.m') diff --git a/Foundation/GTMScriptRunner.m b/Foundation/GTMScriptRunner.m new file mode 100644 index 0000000..30769fb --- /dev/null +++ b/Foundation/GTMScriptRunner.m @@ -0,0 +1,246 @@ +// +// GTMScriptRunner.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMScriptRunner.h" + + +@interface NSTask (SafeLaunching) +- (BOOL)safeLaunch; +@end + +@interface GTMScriptRunner (PrivateMethods) +- (NSTask *)interpreterTaskWithAdditionalArgs:(NSArray *)args; +@end + +@implementation GTMScriptRunner + ++ (GTMScriptRunner *)runner { + return [[[self alloc] init] autorelease]; +} + ++ (GTMScriptRunner *)runnerWithBash { + return [self runnerWithInterpreter:@"/bin/bash"]; +} + ++ (GTMScriptRunner *)runnerWithPerl { + return [self runnerWithInterpreter:@"/usr/bin/perl"]; +} + ++ (GTMScriptRunner *)runnerWithPython { + return [self runnerWithInterpreter:@"/usr/bin/python"]; +} + ++ (GTMScriptRunner *)runnerWithInterpreter:(NSString *)interp { + return [self runnerWithInterpreter:interp withArgs:nil]; +} + ++ (GTMScriptRunner *)runnerWithInterpreter:(NSString *)interp withArgs:(NSArray *)args { + return [[[self alloc] initWithInterpreter:interp withArgs:args] autorelease]; +} + +- (id)init { + return [self initWithInterpreter:nil]; +} + +- (id)initWithInterpreter:(NSString *)interp { + return [self initWithInterpreter:interp withArgs:nil]; +} + +- (id)initWithInterpreter:(NSString *)interp withArgs:(NSArray *)args { + if ((self = [super init])) { + trimsWhitespace_ = YES; + interpreter_ = [interp copy]; + interpreterArgs_ = [args retain]; + if (!interpreter_) { + interpreter_ = @"/bin/sh"; + } + } + return self; +} + +- (void)dealloc { + [interpreter_ release]; + [interpreterArgs_ release]; + [super dealloc]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@<%p>{ interpreter = '%@', args = %@ }", + [self class], self, interpreter_, interpreterArgs_]; +} + +- (NSString *)run:(NSString *)cmds { + return [self run:cmds standardError:nil]; +} + +- (NSString *)run:(NSString *)cmds standardError:(NSString **)err { + if (!cmds) return nil; + + NSTask *task = [self interpreterTaskWithAdditionalArgs:nil]; + NSFileHandle *toTask = [[task standardInput] fileHandleForWriting]; + NSFileHandle *fromTask = [[task standardOutput] fileHandleForReading]; + + if (![task safeLaunch]) { + 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]]; + } + } + + [task terminate]; + + if (trimsWhitespace_) { + output = [output stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } + + if ([output length] < 1) { + output = nil; + } + + return output; +} + +- (NSString *)runScript:(NSString *)path { + return [self runScript:path withArgs:nil]; +} + +- (NSString *)runScript:(NSString *)path withArgs:(NSArray *)args { + return [self runScript:path withArgs:args standardError:nil]; +} + +- (NSString *)runScript:(NSString *)path withArgs:(NSArray *)args standardError:(NSString **)err { + if (!path) return nil; + + NSArray *scriptPlusArgs = [[NSArray arrayWithObject:path] arrayByAddingObjectsFromArray:args]; + NSTask *task = [self interpreterTaskWithAdditionalArgs:scriptPlusArgs]; + NSFileHandle *fromTask = [[task standardOutput] fileHandleForReading]; + + if (![task safeLaunch]) { + return nil; + } + + 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]]; + } + } + + [task terminate]; + + if (trimsWhitespace_) { + output = [output stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } + + if ([output length] < 1) { + output = nil; + } + + return output; +} + +- (NSDictionary *)environment { + return environment_; +} + +- (void)setEnvironment:(NSDictionary *)newEnv { + [environment_ autorelease]; + environment_ = [newEnv retain]; +} + +- (BOOL)trimsWhitespace { + return trimsWhitespace_; +} + +- (void)setTrimsWhitespace:(BOOL)trim { + trimsWhitespace_ = trim; +} + +@end + + +@implementation GTMScriptRunner (PrivateMethods) + +- (NSTask *)interpreterTaskWithAdditionalArgs:(NSArray *)args { + NSTask *task = [[[NSTask alloc] init] autorelease]; + [task setLaunchPath:interpreter_]; + [task setStandardInput:[NSPipe pipe]]; + [task setStandardOutput:[NSPipe pipe]]; + [task setStandardError:[NSPipe pipe]]; + + // If |environment_| is nil, then use an empty dictionary, otherwise use + // environment_ exactly. + [task setEnvironment:(environment_ + ? environment_ + : [NSDictionary dictionary])]; + + // Build args to interpreter. The format is: + // interp [args-to-interp] [script-name [args-to-script]] + NSArray *allArgs = nil; + if (interpreterArgs_) { + allArgs = interpreterArgs_; + } + if (args) { + allArgs = allArgs ? [allArgs arrayByAddingObjectsFromArray:args] : args; + } + if (allArgs){ + [task setArguments:allArgs]; + } + + return task; +} + +@end + +@implementation NSTask (SafeLaunching) + +- (BOOL)safeLaunch { + BOOL isOK = YES; + @try { + [self launch]; + } @catch (id ex) { + isOK = NO; + NSLog(@"Failed to launch interpreter '%@' due to: %@", [self launchPath], ex); + } + return isOK; +} + +@end -- cgit v1.2.3