aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMScriptRunner.m
diff options
context:
space:
mode:
authorGravatar thomasvl <thomasvl@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-02-27 03:54:04 +0000
committerGravatar thomasvl <thomasvl@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-02-27 03:54:04 +0000
commitf38d4ee0e9157d2ac3c9a08fde37b9e19457fb0f (patch)
tree4182025226697a53d884a65a13e94adc44e60c74 /Foundation/GTMScriptRunner.m
parentb3086cfd9aead0b2900c2d942b289957837286ab (diff)
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.
Diffstat (limited to 'Foundation/GTMScriptRunner.m')
-rw-r--r--Foundation/GTMScriptRunner.m246
1 files changed, 246 insertions, 0 deletions
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