aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstuconnolly <stuart02@gmail.com>2012-05-07 11:07:44 +0000
committerstuconnolly <stuart02@gmail.com>2012-05-07 11:07:44 +0000
commit38eae1b30dc9eb65138741079737cb3e5adcc354 (patch)
treec1a390cb0d696450a2b9a4dfc57fd3f96eedb11c
parente638ab4bc8e1414b6333370af07f851bd218f623 (diff)
downloadsequelpro-38eae1b30dc9eb65138741079737cb3e5adcc354.tar.gz
sequelpro-38eae1b30dc9eb65138741079737cb3e5adcc354.tar.bz2
sequelpro-38eae1b30dc9eb65138741079737cb3e5adcc354.zip
Move the BASH command code from the string additions category to remove the dependency on SP specific code and all the tests to build successfully.
-rw-r--r--Source/SPAppController.m26
-rw-r--r--Source/SPBundleCommandRunner.h42
-rw-r--r--Source/SPBundleCommandRunner.m340
-rw-r--r--Source/SPBundleEditorController.m6
-rw-r--r--Source/SPBundleHTMLOutputController.m20
-rw-r--r--Source/SPCopyTable.m19
-rw-r--r--Source/SPStringAdditions.h22
-rw-r--r--Source/SPStringAdditions.m485
-rw-r--r--Source/SPTextView.m6
-rw-r--r--Source/SPTextViewAdditions.m18
-rw-r--r--UnitTests/SPStringAdditionsTest.h2
-rw-r--r--UnitTests/SPStringAdditionsTest.m4
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj8
13 files changed, 553 insertions, 445 deletions
diff --git a/Source/SPAppController.m b/Source/SPAppController.m
index 4d3cc92b..2c03ddf4 100644
--- a/Source/SPAppController.m
+++ b/Source/SPAppController.m
@@ -40,6 +40,7 @@
#import "SPCustomQuery.h"
#import "SPFavoritesController.h"
#import "SPEditorTokens.h"
+#import "SPBundleCommandRunner.h"
#import <PSMTabBar/PSMTabBarControl.h>
#import <Sparkle/Sparkle.h>
@@ -572,7 +573,9 @@ YY_BUFFER_STATE yy_scan_string (const char *);
NSError *error = nil;
NSString *removePath = [[[installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]] objectForKey:@"path"] substringToIndex:([(NSString *)[[installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]] objectForKey:@"path"] length]-[SPBundleFileName length]-1)];
NSString *moveToTrashCommand = [NSString stringWithFormat:@"osascript -e 'tell application \"Finder\" to move (POSIX file \"%@\") to the trash'", removePath];
- [moveToTrashCommand runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&error];
+
+ [SPBundleCommandRunner runBashCommand:moveToTrashCommand withEnvironment:nil atCurrentDirectoryPath:nil error:&error];
+
if(error != nil) {
alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while moving “%@” to Trash.", @"Open Files : Bundle : Already-Installed : Delete-Old-Error : Could not delete old bundle before installing new version."), removePath]
defaultButton:NSLocalizedString(@"OK", @"Open Files : Bundle : Already-Installed : Delete-Old-Error : OK button")
@@ -981,15 +984,15 @@ YY_BUFFER_STATE yy_scan_string (const char *);
return;
}
- NSString *output = [cmd runBashCommandWithEnvironment:env
- atCurrentDirectoryPath:nil
- callerInstance:self
- contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name",
- NSLocalizedString(@"General", @"general menu item label"), @"scope",
- uuid, SPBundleFileInternalexecutionUUID,
- nil]
- error:&err];
+ NSString *output = [SPBundleCommandRunner runBashCommand:cmd
+ withEnvironment:env
+ atCurrentDirectoryPath:nil
+ callerInstance:self
+ contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
+ ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name",
+ NSLocalizedString(@"General", @"general menu item label"), @"scope",
+ uuid, SPBundleFileInternalexecutionUUID, nil]
+ error:&err];
[[NSFileManager defaultManager] removeItemAtPath:bundleInputFilePath error:nil];
@@ -1681,7 +1684,8 @@ YY_BUFFER_STATE yy_scan_string (const char *);
error = nil;
NSString *moveToTrashCommand = [NSString stringWithFormat:@"osascript -e 'tell application \"Finder\" to move (POSIX file \"%@\") to the trash'", oldBundle];
- [moveToTrashCommand runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&error];
+
+ [SPBundleCommandRunner runBashCommand:moveToTrashCommand withEnvironment:nil atCurrentDirectoryPath:nil error:&error];
if(error != nil) {
NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while moving “%@” to Trash.", @"error while moving “%@” to trash"), [[installedBundleUUIDs objectForKey:[cmdDataOld objectForKey:SPBundleFileUUIDKey]] objectForKey:@"path"]]
diff --git a/Source/SPBundleCommandRunner.h b/Source/SPBundleCommandRunner.h
new file mode 100644
index 00000000..5c691c59
--- /dev/null
+++ b/Source/SPBundleCommandRunner.h
@@ -0,0 +1,42 @@
+//
+// $Id$
+//
+// SPBundleCommandRunner.h
+// Sequel Pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on May 6, 2012
+// Copyright (c) 2012 Stuart Connolly. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+@interface SPBundleCommandRunner : NSObject
+
+#ifndef SP_REFACTOR /* run self as bash commands */
+
++ (NSString *)runBashCommand:(NSString *)command withEnvironment:(NSDictionary *)shellEnvironment atCurrentDirectoryPath:(NSString *)path error:(NSError **)theError;
++ (NSString *)runBashCommand:(NSString *)command withEnvironment:(NSDictionary *)shellEnvironment atCurrentDirectoryPath:(NSString *)path callerInstance:(id)caller contextInfo:(NSDictionary *)contextInfo error:(NSError **)theError;
+
+#endif
+
+@end
diff --git a/Source/SPBundleCommandRunner.m b/Source/SPBundleCommandRunner.m
new file mode 100644
index 00000000..5d2336f2
--- /dev/null
+++ b/Source/SPBundleCommandRunner.m
@@ -0,0 +1,340 @@
+//
+// $Id$
+//
+// SPBundleCommandRunner.m
+// Sequel Pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on May 6, 2012
+// Copyright (c) 2012 Stuart Connolly. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "SPBundleCommandRunner.h"
+#import "SPDatabaseDocument.h"
+
+// Defined to suppress warnings
+@interface NSObject (SPBundleMethods)
+
+- (NSString *)lastBundleBlobFilesDirectory;
+- (void)setLastBundleBlobFilesDirectory:(NSString *)path;
+
+@end
+
+// Defined to suppress warnings
+@interface NSObject (SPWindowControllerTabMethods)
+
+- (id)selectedTableDocument;
+
+@end
+
+@implementation SPBundleCommandRunner
+
+#ifndef SP_REFACTOR /* run commands */
+
+/**
+ * Run the supplied string as a BASH command(s) and return the result.
+ * This task can be interrupted by pressing ⌘.
+ *
+ * @param command The command to run
+ * @param shellEnvironment A dictionary of environment variable values whose keys are the variable names.
+ * @param path The current directory for the bash command. If path is nil, the current directory is inherited from the process that created the receiver (normally /).
+ * @param theError If not nil and the bash command failed it contains the returned error message as NSLocalizedDescriptionKey
+ *
+ */
++ (NSString *)runBashCommand:(NSString *)command withEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path error:(NSError**)theError
+{
+ return [SPBundleCommandRunner runBashCommand:command withEnvironment:shellEnvironment atCurrentDirectoryPath:path callerInstance:nil contextInfo:nil error:theError];
+}
+
+/**
+ * Run the supplied command as a BASH command(s) and return the result.
+ * This task can be interrupted by pressing ⌘.
+ *
+ * @param command The command to run
+ * @param shellEnvironment A dictionary of environment variable values whose keys are the variable names.
+ * @param path The current directory for the bash command. If path is nil, the current directory is inherited from the process that created the receiver (normally /).
+ * @param caller The SPDatabaseDocument which invoked that command to register the command for cancelling; if nil the command won't be registered.
+ * @param name The menu title of the command.
+ * @param theError If not nil and the bash command failed it contains the returned error message as NSLocalizedDescriptionKey
+ *
+ */
++ (NSString *)runBashCommand:(NSString *)command withEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path callerInstance:(id)caller contextInfo:(NSDictionary*)contextInfo error:(NSError**)theError
+{
+ NSFileManager *fm = [NSFileManager defaultManager];
+
+ BOOL userTerminated = NO;
+ BOOL redirectForScript = NO;
+ BOOL isDir = NO;
+
+ NSMutableArray *scriptHeaderArguments = [NSMutableArray array];
+ NSString *scriptPath = @"";
+ NSString *uuid = (contextInfo && [contextInfo objectForKey:SPBundleFileInternalexecutionUUID]) ? [contextInfo objectForKey:SPBundleFileInternalexecutionUUID] : [NSString stringWithNewUUID];
+ NSString *stdoutFilePath = [NSString stringWithFormat:@"%@_%@", SPBundleTaskOutputFilePath, uuid];
+ NSString *scriptFilePath = [NSString stringWithFormat:@"%@_%@", SPBundleTaskScriptCommandFilePath, uuid];
+
+ [fm removeItemAtPath:scriptFilePath error:nil];
+ [fm removeItemAtPath:stdoutFilePath error:nil];
+ if([[NSApp delegate] lastBundleBlobFilesDirectory] != nil)
+ [fm removeItemAtPath:[[NSApp delegate] lastBundleBlobFilesDirectory] error:nil];
+
+ if([shellEnvironment objectForKey:SPBundleShellVariableBlobFileDirectory])
+ [[NSApp delegate] setLastBundleBlobFilesDirectory:[shellEnvironment objectForKey:SPBundleShellVariableBlobFileDirectory]];
+
+ // Parse first line for magic header #! ; if found save the script content and run the command after #! with that file.
+ // This allows to write perl, ruby, osascript scripts natively.
+ if([command length] > 3 && [command hasPrefix:@"#!"] && [shellEnvironment objectForKey:SPBundleShellVariableBundlePath]) {
+
+ NSRange firstLineRange = NSMakeRange(2, [command rangeOfString:@"\n"].location - 2);
+
+ [scriptHeaderArguments setArray:[[command substringWithRange:firstLineRange] componentsSeparatedByString:@" "]];
+
+ while([scriptHeaderArguments containsObject:@""])
+ [scriptHeaderArguments removeObject:@""];
+
+ if([scriptHeaderArguments count])
+ scriptPath = [scriptHeaderArguments objectAtIndex:0];
+
+ if([scriptPath hasPrefix:@"/"] && [fm fileExistsAtPath:scriptPath isDirectory:&isDir] && !isDir) {
+ NSString *script = [command substringWithRange:NSMakeRange(NSMaxRange(firstLineRange), [command length] - NSMaxRange(firstLineRange))];
+ NSError *writeError = nil;
+ [script writeToFile:scriptFilePath atomically:YES encoding:NSUTF8StringEncoding error:&writeError];
+ if(writeError == nil) {
+ redirectForScript = YES;
+ [scriptHeaderArguments addObject:scriptFilePath];
+ } else {
+ NSBeep();
+ NSLog(@"Couldn't write script file.");
+ }
+ }
+ } else {
+ [scriptHeaderArguments addObject:@"/bin/sh"];
+ NSError *writeError = nil;
+ [command writeToFile:scriptFilePath atomically:YES encoding:NSUTF8StringEncoding error:&writeError];
+ if(writeError == nil) {
+ redirectForScript = YES;
+ [scriptHeaderArguments addObject:scriptFilePath];
+ } else {
+ NSBeep();
+ NSLog(@"Couldn't write script file.");
+ }
+ }
+
+ NSTask *bashTask = [[NSTask alloc] init];
+ [bashTask setLaunchPath:@"/bin/bash"];
+
+ NSMutableDictionary *theEnv = [NSMutableDictionary dictionary];
+ [theEnv setDictionary:shellEnvironment];
+
+ [theEnv setObject:[[NSBundle mainBundle] pathForResource:@"appicon" ofType:@"icns"] forKey:SPBundleShellVariableIconFile];
+ [theEnv setObject:[NSString stringWithFormat:@"%@/Contents/Resources", [[NSBundle mainBundle] bundlePath]] forKey:SPBundleShellVariableAppResourcesDirectory];
+ [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionNone] forKey:SPBundleShellVariableExitNone];
+ [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionReplaceSection] forKey:SPBundleShellVariableExitReplaceSelection];
+ [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionReplaceContent] forKey:SPBundleShellVariableExitReplaceContent];
+ [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionInsertAsText] forKey:SPBundleShellVariableExitInsertAsText];
+ [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionInsertAsSnippet] forKey:SPBundleShellVariableExitInsertAsSnippet];
+ [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionShowAsHTML] forKey:SPBundleShellVariableExitShowAsHTML];
+ [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionShowAsTextTooltip] forKey:SPBundleShellVariableExitShowAsTextTooltip];
+ [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionShowAsHTMLTooltip] forKey:SPBundleShellVariableExitShowAsHTMLTooltip];
+
+ // Create and set an unique process ID for each SPDatabaseDocument which has to passed
+ // for each sequelpro:// scheme command as user to be able to identify the url scheme command.
+ // Furthermore this id is used to communicate with the called command as file name.
+ id doc = nil;
+ if([[[NSApp mainWindow] delegate] respondsToSelector:@selector(selectedTableDocument)])
+ doc = [[[NSApp mainWindow] delegate] selectedTableDocument];
+ // Check if connected
+ if([doc getConnection] == nil)
+ doc = nil;
+ else {
+ for (NSWindow *aWindow in [NSApp orderedWindows]) {
+ if([[[[aWindow windowController] class] description] isEqualToString:@"SPWindowController"]) {
+ if([[[aWindow windowController] documents] count] && [[[[[[aWindow windowController] documents] objectAtIndex:0] class] description] isEqualToString:@"SPDatabaseDocument"]) {
+ // Check if connected
+ if([[[[aWindow windowController] documents] objectAtIndex:0] getConnection])
+ doc = [[[aWindow windowController] documents] objectAtIndex:0];
+ else
+ doc = nil;
+ }
+ }
+ if(doc) break;
+ }
+ }
+
+ if(doc != nil) {
+
+ [doc setProcessID:uuid];
+
+ [theEnv setObject:uuid forKey:SPBundleShellVariableProcessID];
+ [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, uuid] forKey:SPBundleShellVariableQueryFile];
+ [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, uuid] forKey:SPBundleShellVariableQueryResultFile];
+ [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, uuid] forKey:SPBundleShellVariableQueryResultStatusFile];
+ [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, uuid] forKey:SPBundleShellVariableQueryResultMetaFile];
+
+ if([doc shellVariables])
+ [theEnv addEntriesFromDictionary:[doc shellVariables]];
+
+ if([theEnv objectForKey:SPBundleShellVariableCurrentEditedColumnName] && [[theEnv objectForKey:SPBundleShellVariableDataTableSource] isEqualToString:@"content"])
+ [theEnv setObject:[theEnv objectForKey:SPBundleShellVariableSelectedTable] forKey:SPBundleShellVariableCurrentEditedTable];
+
+ }
+
+ if(theEnv != nil && [theEnv count])
+ [bashTask setEnvironment:theEnv];
+
+ if(path != nil)
+ [bashTask setCurrentDirectoryPath:path];
+ else if([shellEnvironment objectForKey:SPBundleShellVariableBundlePath] && [fm fileExistsAtPath:[shellEnvironment objectForKey:SPBundleShellVariableBundlePath] isDirectory:&isDir] && isDir)
+ [bashTask setCurrentDirectoryPath:[shellEnvironment objectForKey:SPBundleShellVariableBundlePath]];
+
+ // STDOUT will be redirected to SPBundleTaskOutputFilePath in order to avoid nasty pipe programming due to block size reading
+ if([shellEnvironment objectForKey:SPBundleShellVariableInputFilePath])
+ [bashTask setArguments:[NSArray arrayWithObjects:@"-c", [NSString stringWithFormat:@"%@ > %@ < %@", [scriptHeaderArguments componentsJoinedByString:@" "], stdoutFilePath, [shellEnvironment objectForKey:SPBundleShellVariableInputFilePath]], nil]];
+ else
+ [bashTask setArguments:[NSArray arrayWithObjects:@"-c", [NSString stringWithFormat:@"%@ > %@", [scriptHeaderArguments componentsJoinedByString:@" "], stdoutFilePath], nil]];
+
+ NSPipe *stderr_pipe = [NSPipe pipe];
+ [bashTask setStandardError:stderr_pipe];
+ NSFileHandle *stderr_file = [stderr_pipe fileHandleForReading];
+ [bashTask launch];
+ NSInteger pid = -1;
+ if(caller != nil && [caller respondsToSelector:@selector(registerActivity:)]) {
+ // register command
+ pid = [bashTask processIdentifier];
+ NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:pid], @"pid",
+ (contextInfo)?:[NSDictionary dictionary], @"contextInfo",
+ @"bashcommand", @"type",
+ [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]], @"starttime",
+ nil];
+ [caller registerActivity:dict];
+ }
+
+ // Listen to ⌘. to terminate
+ while(1) {
+ if(![bashTask isRunning] || [bashTask processIdentifier] == 0) break;
+ NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ usleep(1000);
+ if(!event) continue;
+ if ([event type] == NSKeyDown) {
+ unichar key = [[event characters] length] == 1 ? [[event characters] characterAtIndex:0] : 0;
+ if (([event modifierFlags] & NSCommandKeyMask) && key == '.') {
+ [bashTask terminate];
+ userTerminated = YES;
+ break;
+ }
+ [NSApp sendEvent:event];
+ } else {
+ [NSApp sendEvent:event];
+ }
+ }
+
+ [bashTask waitUntilExit];
+
+ // unregister BASH command if it was registered
+ if(pid > 0) {
+ [caller removeRegisteredActivity:pid];
+ }
+
+ // Remove files
+ [fm removeItemAtPath:scriptFilePath error:nil];
+ if([theEnv objectForKey:SPBundleShellVariableQueryFile])
+ [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableQueryFile] error:nil];
+ if([theEnv objectForKey:SPBundleShellVariableQueryResultFile])
+ [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableQueryResultFile] error:nil];
+ if([theEnv objectForKey:SPBundleShellVariableQueryResultStatusFile])
+ [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableQueryResultStatusFile] error:nil];
+ if([theEnv objectForKey:SPBundleShellVariableQueryResultMetaFile])
+ [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableQueryResultMetaFile] error:nil];
+ if([theEnv objectForKey:SPBundleShellVariableInputTableMetaData])
+ [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableInputTableMetaData] error:nil];
+
+ // If return from bash re-activate Sequel Pro
+ [NSApp activateIgnoringOtherApps:YES];
+
+ NSInteger status = [bashTask terminationStatus];
+ NSData *errdata = [stderr_file readDataToEndOfFile];
+
+ // Check STDERR
+ if([errdata length] && (status < SPBundleRedirectActionNone || status > SPBundleRedirectActionLastCode)) {
+ [fm removeItemAtPath:stdoutFilePath error:nil];
+
+ if(status == 9 || userTerminated) return @"";
+ if(theError != NULL) {
+ NSMutableString *errMessage = [[[NSMutableString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] autorelease];
+ [errMessage replaceOccurrencesOfString:[NSString stringWithFormat:@"%@: ", scriptFilePath] withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [errMessage length])];
+ *theError = [[[NSError alloc] initWithDomain:NSPOSIXErrorDomain
+ code:status
+ userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
+ errMessage,
+ NSLocalizedDescriptionKey,
+ nil]] autorelease];
+ } else {
+ NSBeep();
+ }
+ return @"";
+ }
+
+ // Read STDOUT saved to file
+ if([fm fileExistsAtPath:stdoutFilePath isDirectory:nil]) {
+ NSString *stdoutContent = [NSString stringWithContentsOfFile:stdoutFilePath encoding:NSUTF8StringEncoding error:nil];
+ if(bashTask) [bashTask release], bashTask = nil;
+ [fm removeItemAtPath:stdoutFilePath error:nil];
+ if(stdoutContent != nil) {
+ if (status == 0) {
+ return stdoutContent;
+ } else {
+ if(theError != NULL) {
+ if(status == 9 || userTerminated) return @"";
+ NSMutableString *errMessage = [[[NSMutableString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] autorelease];
+ [errMessage replaceOccurrencesOfString:SPBundleTaskScriptCommandFilePath withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [errMessage length])];
+ *theError = [[[NSError alloc] initWithDomain:NSPOSIXErrorDomain
+ code:status
+ userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
+ errMessage,
+ NSLocalizedDescriptionKey,
+ nil]] autorelease];
+ } else {
+ NSBeep();
+ }
+ if(status > SPBundleRedirectActionNone && status <= SPBundleRedirectActionLastCode)
+ return stdoutContent;
+ else
+ return @"";
+ }
+ } else {
+ NSLog(@"Couldn't read return string from “%@” by using UTF-8 encoding.", command);
+ NSBeep();
+ }
+ }
+
+ if (bashTask) [bashTask release];
+ [fm removeItemAtPath:stdoutFilePath error:nil];
+ return @"";
+}
+
+#endif
+
+@end
diff --git a/Source/SPBundleEditorController.m b/Source/SPBundleEditorController.m
index 8d4d1c65..b807a52d 100644
--- a/Source/SPBundleEditorController.m
+++ b/Source/SPBundleEditorController.m
@@ -23,8 +23,8 @@
// More info at <http://code.google.com/p/sequel-pro/>
#import "SPBundleEditorController.h"
-#import "SPArrayAdditions.h"
#import "SPMenuAdditions.h"
+#import "SPBundleCommandRunner.h"
#define kBundleNameKey @"bundleName"
#define kChildrenKey @"_children_"
@@ -1021,7 +1021,9 @@
// Use a AppleScript script since NSWorkspace performFileOperation or NSFileManager moveItemAtPath
// have problems probably due access rights.
NSString *moveToTrashCommand = [NSString stringWithFormat:@"osascript -e 'tell application \"Finder\" to move (POSIX file \"%@\") to the trash'", thePath];
- [moveToTrashCommand runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&error];
+
+ [SPBundleCommandRunner runBashCommand:moveToTrashCommand withEnvironment:nil atCurrentDirectoryPath:nil error:&error];
+
if(error != nil) {
NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while moving “%@” to Trash.", @"Bundle Editor : Trash-Bundle(s)-Error : error dialog title"), thePath]
defaultButton:NSLocalizedString(@"OK", @"Bundle Editor : Trash-Bundle(s)-Error : OK button")
diff --git a/Source/SPBundleHTMLOutputController.m b/Source/SPBundleHTMLOutputController.m
index cff150c0..d31a5d28 100644
--- a/Source/SPBundleHTMLOutputController.m
+++ b/Source/SPBundleHTMLOutputController.m
@@ -26,6 +26,7 @@
#import "SPAlertSheets.h"
#import "SPPrintAccessory.h"
#import "SPAppController.h"
+#import "SPBundleCommandRunner.h"
@class WebScriptCallFrame;
@@ -659,7 +660,7 @@
NSString *output = nil;
if(uuid == nil)
- output = [command runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&err];
+ output = [SPBundleCommandRunner runBashCommand:command withEnvironment:nil atCurrentDirectoryPath:nil error:&err];
else {
NSMutableDictionary *theEnv = [NSMutableDictionary dictionary];
[theEnv addEntriesFromDictionary:[[NSApp delegate] shellEnvironmentForDocument:nil]];
@@ -668,15 +669,16 @@
[theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, uuid] forKey:SPBundleShellVariableQueryResultFile];
[theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, uuid] forKey:SPBundleShellVariableQueryResultStatusFile];
[theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, uuid] forKey:SPBundleShellVariableQueryResultMetaFile];
- output = [command runBashCommandWithEnvironment:theEnv
+
+ output = [SPBundleCommandRunner runBashCommand:command
+ withEnvironment:theEnv
atCurrentDirectoryPath:nil
- callerInstance:[NSApp delegate]
- contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- @"JavaScript", @"name",
- NSLocalizedString(@"General", @"general menu item label"), @"scope",
- uuid, SPBundleFileInternalexecutionUUID,
- nil]
- error:&err];
+ callerInstance:[NSApp delegate]
+ contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
+ @"JavaScript", @"name",
+ NSLocalizedString(@"General", @"general menu item label"), @"scope",
+ uuid, SPBundleFileInternalexecutionUUID, nil]
+ error:&err];
}
if(err != nil) {
diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m
index ea0fe720..edc29db9 100644
--- a/Source/SPCopyTable.m
+++ b/Source/SPCopyTable.m
@@ -40,6 +40,8 @@
#import "SPAppController.h"
#endif
#import "SPTablesList.h"
+#import "SPBundleCommandRunner.h"
+
#import <SPMySQL/SPMySQL.h>
NSInteger SPEditMenuCopy = 2001;
@@ -1407,15 +1409,14 @@ static const NSInteger kBlobAsImageFile = 4;
}
- NSString *output = [cmd runBashCommandWithEnvironment:env
- atCurrentDirectoryPath:nil
- callerInstance:[[NSApp delegate] frontDocument]
- contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name",
- NSLocalizedString(@"Data Table", @"data table menu item label"), @"scope",
- uuid, SPBundleFileInternalexecutionUUID,
- nil]
- error:&err];
+ NSString *output = [SPBundleCommandRunner runBashCommand:cmd withEnvironment:env
+ atCurrentDirectoryPath:nil
+ callerInstance:[[NSApp delegate] frontDocument]
+ contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
+ ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name",
+ NSLocalizedString(@"Data Table", @"data table menu item label"), @"scope",
+ uuid, SPBundleFileInternalexecutionUUID, nil]
+ error:&err];
[[NSFileManager defaultManager] removeItemAtPath:bundleInputFilePath error:nil];
diff --git a/Source/SPStringAdditions.h b/Source/SPStringAdditions.h
index a45fbe0b..83ab2470 100644
--- a/Source/SPStringAdditions.h
+++ b/Source/SPStringAdditions.h
@@ -23,12 +23,12 @@
//
// More info at <http://code.google.com/p/sequel-pro/>
-/*
+/**
* NSStringUTF8String(@"a String") function can be used to speed up
* the convertion from a NSString to NSData or const char* resp.
* NSData *d = [aStr UTF8String]; :== NSData *d = NSStringUTF8String(aStr);
*/
-static inline const char* NSStringUTF8String(NSString* self)
+static inline const char *NSStringUTF8String(NSString *self)
{
typedef const char* (*SPUTF8StringMethodPtr)(NSString*, SEL);
static SPUTF8StringMethodPtr SPNSStringGetUTF8String;
@@ -37,7 +37,7 @@ static inline const char* NSStringUTF8String(NSString* self)
return to_return;
}
-static inline void NSMutableAttributedStringAddAttributeValueRange (NSMutableAttributedString* self, NSString* aStr, id aValue, NSRange aRange)
+static inline void NSMutableAttributedStringAddAttributeValueRange(NSMutableAttributedString *self, NSString *aStr, id aValue, NSRange aRange)
{
typedef void (*SPMutableAttributedStringAddAttributeValueRangeMethodPtr)(NSMutableAttributedString*, SEL, NSString*, id, NSRange);
static SPMutableAttributedStringAddAttributeValueRangeMethodPtr SPMutableAttributedStringAddAttributeValueRange;
@@ -46,7 +46,7 @@ static inline void NSMutableAttributedStringAddAttributeValueRange (NSMutableAtt
return;
}
-static inline id NSMutableAttributedStringAttributeAtIndex (NSMutableAttributedString* self, NSString* aStr, NSUInteger anIndex, NSRangePointer aRange)
+static inline id NSMutableAttributedStringAttributeAtIndex(NSMutableAttributedString *self, NSString *aStr, NSUInteger anIndex, NSRangePointer aRange)
{
typedef id (*SPMutableAttributedStringAttributeAtIndexMethodPtr)(NSMutableAttributedString*, SEL, NSString*, NSUInteger, NSRangePointer);
static SPMutableAttributedStringAttributeAtIndexMethodPtr SPMutableAttributedStringAttributeAtIndex;
@@ -59,8 +59,7 @@ static inline id NSMutableAttributedStringAttributeAtIndex (NSMutableAttributedS
+ (NSString *)stringForByteSize:(long long)byteSize;
+ (NSString *)stringForTimeInterval:(double)timeInterval;
-+ (NSString*)stringWithNewUUID;
-
++ (NSString *)stringWithNewUUID;
- (NSString *)rot13;
- (NSString *)HTMLEscapeString;
@@ -70,16 +69,11 @@ static inline id NSMutableAttributedStringAttributeAtIndex (NSMutableAttributedS
- (NSArray *)lineRangesForRange:(NSRange)aRange;
- (NSString *)createViewSyntaxPrettifier;
-- (NSString*)getGeomFromTextString;
+- (NSString *)getGeomFromTextString;
-- (NSString *)stringByRemovingCharactersInSet:(NSCharacterSet*)charSet options:(NSUInteger)mask;
-- (NSString *)stringByRemovingCharactersInSet:(NSCharacterSet*)charSet;
+- (NSString *)stringByRemovingCharactersInSet:(NSCharacterSet *)charSet;
+- (NSString *)stringByRemovingCharactersInSet:(NSCharacterSet *)charSet options:(NSUInteger)mask;
- (CGFloat)levenshteinDistanceWithWord:(NSString *)stringB;
-#ifndef SP_REFACTOR /* run self as bash commands */
-- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path callerInstance:(id)caller contextInfo:(NSDictionary*)contextInfo error:(NSError**)theError;
-- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path error:(NSError**)theError;
-#endif
-
@end
diff --git a/Source/SPStringAdditions.m b/Source/SPStringAdditions.m
index 31dbbfc6..40a03e5c 100644
--- a/Source/SPStringAdditions.m
+++ b/Source/SPStringAdditions.m
@@ -25,26 +25,10 @@
#import "SPStringAdditions.h"
#import "RegexKitLite.h"
-#import "SPDatabaseDocument.h"
-
-// Defined to suppress warnings
-@interface NSObject (SPBundleMethods)
-
-- (NSString *)lastBundleBlobFilesDirectory;
-- (void)setLastBundleBlobFilesDirectory:(NSString *)path;
-
-@end
-
-// Defined to suppress warnings
-@interface NSObject (SPWindowControllerTabMethods)
-
-- (id)selectedTableDocument;
-
-@end
@interface NSString (PrivateAPI)
-- (NSInteger)smallestOf:(NSInteger)a andOf:(NSInteger)b andOf:(NSInteger)c;
+- (NSInteger)_smallestOf:(NSInteger)a andOf:(NSInteger)b andOf:(NSInteger)c;
@end
@@ -153,6 +137,7 @@
}
timeInterval = (timeInterval / 3600);
+
[numberFormatter setFormat:@"#,##0 hours"];
return [numberFormatter stringFromNumber:[NSNumber numberWithDouble:timeInterval]];
@@ -161,45 +146,53 @@
/**
* Returns a new created UUID string.
*/
-+ (NSString*)stringWithNewUUID
++ (NSString* )stringWithNewUUID
{
// Create a new UUID
CFUUIDRef uuidObj = CFUUIDCreate(nil);
// Get the string representation of the UUID
- NSString *newUUID = (NSString*)CFUUIDCreateString(nil, uuidObj);
+ NSString *newUUID = (NSString *)CFUUIDCreateString(nil, uuidObj);
+
CFRelease(uuidObj);
+
return [newUUID autorelease];
}
/**
- * Returns ROT13 representation
+ * Returns the ROT13 representation of self.
*/
- (NSString *)rot13
{
- NSMutableString *holder = [[NSMutableString alloc] init];
unichar theChar;
- NSUInteger i;
-
- for(i = 0; i < [self length]; i++) {
+ NSMutableString *holder = [[NSMutableString alloc] init];
+
+ for (NSUInteger i = 0; i < [self length]; i++)
+ {
theChar = [self characterAtIndex:i];
- if(theChar <= 122 && theChar >= 97) {
- if(theChar + 13 > 122)
+
+ if (theChar <= 122 && theChar >= 97) {
+ if (theChar + 13 > 122) {
theChar -= 13;
- else
+ }
+ else {
theChar += 13;
+ }
+
[holder appendFormat:@"%C", (char)theChar];
-
- } else if(theChar <= 90 && theChar >= 65) {
- if((int)theChar + 13 > 90)
+ }
+ else if (theChar <= 90 && theChar >= 65) {
+ if ((int)theChar + 13 > 90) {
theChar -= 13;
- else
+ }
+ else {
theChar += 13;
+ }
[holder appendFormat:@"%C", theChar];
-
- } else {
+ }
+ else {
[holder appendFormat:@"%C", theChar];
}
}
@@ -234,9 +227,10 @@
}
/**
- * Returns the string quoted with backticks as required for MySQL identifiers
- * eg.: tablename => `tablename`
- * my`table => `my``table`
+ * Returns the string quoted with backticks as required for MySQL identifiers.
+ *
+ * eg.: tablename => `tablename`
+ * my`table => `my``table`
*/
- (NSString *)backtickQuotedString
{
@@ -244,9 +238,10 @@
}
/**
- * Returns the string quoted with ticks as required for MySQL identifiers
- * eg.: tablename => 'tablename'
- * my'table => 'my''table'
+ * Returns the string quoted with ticks as required for MySQL identifiers.
+ *
+ * eg.: tablename => 'tablename'
+ * my'table => 'my''table'
*/
- (NSString *)tickQuotedString
{
@@ -254,7 +249,7 @@
}
/**
- *
+ * Replaces an occurrences of underscores with a single space.
*/
- (NSString *)replaceUnderscoreWithSpace
{
@@ -262,7 +257,8 @@
}
/**
- * Returns a 'CREATE VIEW SYNTAX' string a bit more readable
+ * Returns a more readable version of a 'CREATE VIEW SYNTAX' string.
+ *
* If the string doesn't match it returns the unchanged string.
*/
- (NSString *)createViewSyntaxPrettifier
@@ -273,31 +269,30 @@
NSMutableString *tblSyntax = [NSMutableString stringWithCapacity:[self length]];
NSString * re = @"(.*?) AS select (.*?) (from.*)";
- // create view syntax
+ // Create view syntax
matchedRange = [self rangeOfRegex:re options:(RKLMultiline|RKLDotAll) inRange:searchRange capture:1 error:&err];
- if(!matchedRange.length || matchedRange.length > [self length]) return([self description]);
+ if (!matchedRange.length || matchedRange.length > [self length]) return([self description]);
[tblSyntax appendString:[self substringWithRange:matchedRange]];
[tblSyntax appendString:@"\nAS select\n "];
- // match all column definitions, split them by ',', and rejoin them by '\n'
+ // Match all column definitions, split them by ',', and rejoin them by '\n'
matchedRange = [self rangeOfRegex:re options:(RKLMultiline|RKLDotAll) inRange:searchRange capture:2 error:&err];
- if(!matchedRange.length || matchedRange.length > [self length]) return([self description]);
+ if (!matchedRange.length || matchedRange.length > [self length]) return([self description]);
- [tblSyntax appendString:
- [[[self substringWithRange:matchedRange] componentsSeparatedByString:@"`,`"] componentsJoinedByString:@"`,\n `"]];
+ [tblSyntax appendString:[[[self substringWithRange:matchedRange] componentsSeparatedByString:@"`,`"] componentsJoinedByString:@"`,\n `"]];
- // from ... at a new line
+ // From ... at a new line
matchedRange = [self rangeOfRegex:re options:(RKLMultiline|RKLDotAll) inRange:searchRange capture:3 error:&err];
- if(!matchedRange.length || matchedRange.length > [self length]) return([self description]);
+ if (!matchedRange.length || matchedRange.length > [self length]) return([self description]);
[tblSyntax appendString:@"\n"];
[tblSyntax appendString:[self substringWithRange:matchedRange]];
- // where clause at a new line if given
+ // Where clause at a new line if given
[tblSyntax replaceOccurrencesOfString:@" where (" withString:@"\nwhere (" options:NSLiteralSearch range:NSMakeRange(0, [tblSyntax length])];
return(tblSyntax);
@@ -312,20 +307,24 @@
*/
- (NSArray *)lineRangesForRange:(NSRange)aRange
{
- NSMutableArray *lineRangesArray = [NSMutableArray array];
NSRange currentLineRange;
+ NSMutableArray *lineRangesArray = [NSMutableArray array];
// Check that the range supplied is valid - if not return an empty array.
- if (aRange.location == NSNotFound || aRange.location + aRange.length > [self length])
+ if (aRange.location == NSNotFound || aRange.location + aRange.length > [self length]) {
return lineRangesArray;
+ }
// Get the range of the first string covered by the specified range, and add it to the array
currentLineRange = [self lineRangeForRange:NSMakeRange(aRange.location, 0)];
+
[lineRangesArray addObject:NSStringFromRange(currentLineRange)];
// Loop through until the line end matches or surpasses the end of the specified range
- while (currentLineRange.location + currentLineRange.length < aRange.location + aRange.length) {
+ while (currentLineRange.location + currentLineRange.length < aRange.location + aRange.length)
+ {
currentLineRange = [self lineRangeForRange:NSMakeRange(currentLineRange.location + currentLineRange.length, 0)];
+
[lineRangesArray addObject:NSStringFromRange(currentLineRange)];
}
@@ -338,12 +337,11 @@
*/
- (NSString *)stringByRemovingCharactersInSet:(NSCharacterSet *)charSet options:(NSUInteger)mask
{
- NSRange range;
- NSMutableString* newString = [NSMutableString string];
- NSUInteger len = [self length];
+ NSUInteger len = [self length];
+ NSMutableString *newString = [NSMutableString string];
mask &= ~NSBackwardsSearch;
- range = NSMakeRange (0, len);
+ NSRange range = NSMakeRange (0, len);
while (range.length)
{
@@ -351,12 +349,14 @@
NSUInteger pos = range.location;
range = [self rangeOfCharacterFromSet:charSet options:mask range:range];
- if (range.location == NSNotFound)
+
+ if (range.location == NSNotFound) {
range = NSMakeRange (len, 0);
+ }
- substringRange = NSMakeRange (pos, range.location - pos);
- [newString appendString:[self
- substringWithRange:substringRange]];
+ substringRange = NSMakeRange(pos, range.location - pos);
+
+ [newString appendString:[self substringWithRange:substringRange]];
range.location += range.length;
range.length = len - range.location;
@@ -374,16 +374,16 @@
}
/**
- * Calculate the distance between two string case-insensitively
+ * Calculate the distance between two string case-insensitively.
*/
- (CGFloat)levenshteinDistanceWithWord:(NSString *)stringB
{
- // normalize strings
+ // Normalize strings
NSString * stringA = [NSString stringWithString: self];
- [stringA stringByTrimmingCharactersInSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]];
- [stringB stringByTrimmingCharactersInSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ [stringA stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ [stringB stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
stringA = [stringA lowercaseString];
stringB = [stringB lowercaseString];
@@ -392,32 +392,33 @@
NSInteger n = [stringA length];
NSInteger m = [stringB length];
- if( n++ != 0 && m++ != 0 ) {
-
- d = malloc( sizeof(NSInteger) * m * n );
+ if (n++ != 0 && m++ != 0)
+ {
+ d = malloc(sizeof(NSInteger) * m * n);
- for( k = 0; k < n; k++)
+ for (k = 0; k < n; k++)
+ {
d[k] = k;
+ }
- for( k = 0; k < m; k++)
- d[ k * n ] = k;
-
- for( i = 1; i < n; i++ )
- for( j = 1; j < m; j++ ) {
+ for (k = 0; k < m; k++) {
+ d[ k * n ] = k;
+ }
- if( [stringA characterAtIndex: i-1] == [stringB characterAtIndex: j-1] )
- cost = 0;
- else
- cost = 1;
+ for (i = 1; i < n; i++)
+ for (j = 1; j < m; j++)
+ {
+ cost = ([stringA characterAtIndex:i - 1] == [stringB characterAtIndex:j - 1]) ? 0 : 1;
- d[ j * n + i ] = [self smallestOf: d [ (j - 1) * n + i ] + 1
- andOf: d[ j * n + i - 1 ] + 1
- andOf: d[ (j - 1) * n + i -1 ] + cost ];
+ d[j * n + i] =
+ [self _smallestOf:d[(j - 1) * n + i] + 1
+ andOf:d[j * n + i - 1] + 1
+ andOf:d[(j - 1) * n + i -1] + cost];
}
- distance = d[ n * m - 1 ];
+ distance = d[n * m - 1];
- free( d );
+ free(d);
return distance;
}
@@ -430,322 +431,32 @@
*/
- (NSString*)getGeomFromTextString
{
-
NSString *geomStr = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- if(![self rangeOfString:@")"].length || [self length] < 5) return @"NULL";
+ if (![self rangeOfString:@")"].length || [self length] < 5) return @"NULL";
// No SRID
- if([geomStr hasSuffix:@")"])
+ if ([geomStr hasSuffix:@")"]) {
return [NSString stringWithFormat:@"GeomFromText('%@')", geomStr];
- // Has SRID
- else {
- NSUInteger idx = [geomStr length]-1;
- while(idx>1) {
- if([geomStr characterAtIndex:idx] == ')')
- break;
- idx--;
- }
- return [NSString stringWithFormat:@"GeomFromText('%@'%@)",
- [geomStr substringToIndex:idx+1], [geomStr substringFromIndex:idx+1]];
- }
-
-}
-
-#ifndef SP_REFACTOR /* run self as bash commands */
-/**
- * Run self as BASH command(s) and return the result.
- * This task can be interrupted by pressing ⌘.
- *
- * @param shellEnvironment A dictionary of environment variable values whose keys are the variable names.
- *
- * @param path The current directory for the bash command. If path is nil, the current directory is inherited from the process that created the receiver (normally /).
- *
- * @param caller The SPDatabaseDocument which invoked that command to register the command for cancelling; if nil the command won't be registered.
- *
- * @param name The menu title of the command.
- *
- * @param theError If not nil and the bash command failed it contains the returned error message as NSLocalizedDescriptionKey
- *
- */
-- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path callerInstance:(id)caller contextInfo:(NSDictionary*)contextInfo error:(NSError**)theError
-{
-
- NSFileManager *fm = [NSFileManager defaultManager];
-
- BOOL userTerminated = NO;
- BOOL redirectForScript = NO;
- BOOL isDir = NO;
-
- NSMutableArray *scriptHeaderArguments = [NSMutableArray array];
- NSString *scriptPath = @"";
- NSString *uuid = (contextInfo && [contextInfo objectForKey:SPBundleFileInternalexecutionUUID]) ? [contextInfo objectForKey:SPBundleFileInternalexecutionUUID] : [NSString stringWithNewUUID];
- NSString *stdoutFilePath = [NSString stringWithFormat:@"%@_%@", SPBundleTaskOutputFilePath, uuid];
- NSString *scriptFilePath = [NSString stringWithFormat:@"%@_%@", SPBundleTaskScriptCommandFilePath, uuid];
-
- [fm removeItemAtPath:scriptFilePath error:nil];
- [fm removeItemAtPath:stdoutFilePath error:nil];
- if([[NSApp delegate] lastBundleBlobFilesDirectory] != nil)
- [fm removeItemAtPath:[[NSApp delegate] lastBundleBlobFilesDirectory] error:nil];
-
- if([shellEnvironment objectForKey:SPBundleShellVariableBlobFileDirectory])
- [[NSApp delegate] setLastBundleBlobFilesDirectory:[shellEnvironment objectForKey:SPBundleShellVariableBlobFileDirectory]];
-
- // Parse first line for magic header #! ; if found save the script content and run the command after #! with that file.
- // This allows to write perl, ruby, osascript scripts natively.
- if([self length] > 3 && [self hasPrefix:@"#!"] && [shellEnvironment objectForKey:SPBundleShellVariableBundlePath]) {
-
- NSRange firstLineRange = NSMakeRange(2, [self rangeOfString:@"\n"].location - 2);
-
- [scriptHeaderArguments setArray:[[self substringWithRange:firstLineRange] componentsSeparatedByString:@" "]];
-
- while([scriptHeaderArguments containsObject:@""])
- [scriptHeaderArguments removeObject:@""];
-
- if([scriptHeaderArguments count])
- scriptPath = [scriptHeaderArguments objectAtIndex:0];
-
- if([scriptPath hasPrefix:@"/"] && [fm fileExistsAtPath:scriptPath isDirectory:&isDir] && !isDir) {
- NSString *script = [self substringWithRange:NSMakeRange(NSMaxRange(firstLineRange), [self length] - NSMaxRange(firstLineRange))];
- NSError *writeError = nil;
- [script writeToFile:scriptFilePath atomically:YES encoding:NSUTF8StringEncoding error:&writeError];
- if(writeError == nil) {
- redirectForScript = YES;
- [scriptHeaderArguments addObject:scriptFilePath];
- } else {
- NSBeep();
- NSLog(@"Couldn't write script file.");
- }
- }
- } else {
- [scriptHeaderArguments addObject:@"/bin/sh"];
- NSError *writeError = nil;
- [self writeToFile:scriptFilePath atomically:YES encoding:NSUTF8StringEncoding error:&writeError];
- if(writeError == nil) {
- redirectForScript = YES;
- [scriptHeaderArguments addObject:scriptFilePath];
- } else {
- NSBeep();
- NSLog(@"Couldn't write script file.");
- }
}
-
- NSTask *bashTask = [[NSTask alloc] init];
- [bashTask setLaunchPath:@"/bin/bash"];
-
- NSMutableDictionary *theEnv = [NSMutableDictionary dictionary];
- [theEnv setDictionary:shellEnvironment];
-
- [theEnv setObject:[[NSBundle mainBundle] pathForResource:@"appicon" ofType:@"icns"] forKey:SPBundleShellVariableIconFile];
- [theEnv setObject:[NSString stringWithFormat:@"%@/Contents/Resources", [[NSBundle mainBundle] bundlePath]] forKey:SPBundleShellVariableAppResourcesDirectory];
- [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionNone] forKey:SPBundleShellVariableExitNone];
- [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionReplaceSection] forKey:SPBundleShellVariableExitReplaceSelection];
- [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionReplaceContent] forKey:SPBundleShellVariableExitReplaceContent];
- [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionInsertAsText] forKey:SPBundleShellVariableExitInsertAsText];
- [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionInsertAsSnippet] forKey:SPBundleShellVariableExitInsertAsSnippet];
- [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionShowAsHTML] forKey:SPBundleShellVariableExitShowAsHTML];
- [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionShowAsTextTooltip] forKey:SPBundleShellVariableExitShowAsTextTooltip];
- [theEnv setObject:[NSNumber numberWithInteger:SPBundleRedirectActionShowAsHTMLTooltip] forKey:SPBundleShellVariableExitShowAsHTMLTooltip];
-
- // Create and set an unique process ID for each SPDatabaseDocument which has to passed
- // for each sequelpro:// scheme command as user to be able to identify the url scheme command.
- // Furthermore this id is used to communicate with the called command as file name.
- id doc = nil;
- if([[[NSApp mainWindow] delegate] respondsToSelector:@selector(selectedTableDocument)])
- doc = [[[NSApp mainWindow] delegate] selectedTableDocument];
- // Check if connected
- if([doc getConnection] == nil)
- doc = nil;
else {
- for (NSWindow *aWindow in [NSApp orderedWindows]) {
- if([[[[aWindow windowController] class] description] isEqualToString:@"SPWindowController"]) {
- if([[[aWindow windowController] documents] count] && [[[[[[aWindow windowController] documents] objectAtIndex:0] class] description] isEqualToString:@"SPDatabaseDocument"]) {
- // Check if connected
- if([[[[aWindow windowController] documents] objectAtIndex:0] getConnection])
- doc = [[[aWindow windowController] documents] objectAtIndex:0];
- else
- doc = nil;
- }
- }
- if(doc) break;
- }
- }
-
- if(doc != nil) {
-
- [doc setProcessID:uuid];
-
- [theEnv setObject:uuid forKey:SPBundleShellVariableProcessID];
- [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, uuid] forKey:SPBundleShellVariableQueryFile];
- [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, uuid] forKey:SPBundleShellVariableQueryResultFile];
- [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, uuid] forKey:SPBundleShellVariableQueryResultStatusFile];
- [theEnv setObject:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, uuid] forKey:SPBundleShellVariableQueryResultMetaFile];
-
- if([doc shellVariables])
- [theEnv addEntriesFromDictionary:[doc shellVariables]];
-
- if([theEnv objectForKey:SPBundleShellVariableCurrentEditedColumnName] && [[theEnv objectForKey:SPBundleShellVariableDataTableSource] isEqualToString:@"content"])
- [theEnv setObject:[theEnv objectForKey:SPBundleShellVariableSelectedTable] forKey:SPBundleShellVariableCurrentEditedTable];
-
- }
-
- if(theEnv != nil && [theEnv count])
- [bashTask setEnvironment:theEnv];
-
- if(path != nil)
- [bashTask setCurrentDirectoryPath:path];
- else if([shellEnvironment objectForKey:SPBundleShellVariableBundlePath] && [fm fileExistsAtPath:[shellEnvironment objectForKey:SPBundleShellVariableBundlePath] isDirectory:&isDir] && isDir)
- [bashTask setCurrentDirectoryPath:[shellEnvironment objectForKey:SPBundleShellVariableBundlePath]];
-
- // STDOUT will be redirected to SPBundleTaskOutputFilePath in order to avoid nasty pipe programming due to block size reading
- if([shellEnvironment objectForKey:SPBundleShellVariableInputFilePath])
- [bashTask setArguments:[NSArray arrayWithObjects:@"-c", [NSString stringWithFormat:@"%@ > %@ < %@", [scriptHeaderArguments componentsJoinedByString:@" "], stdoutFilePath, [shellEnvironment objectForKey:SPBundleShellVariableInputFilePath]], nil]];
- else
- [bashTask setArguments:[NSArray arrayWithObjects:@"-c", [NSString stringWithFormat:@"%@ > %@", [scriptHeaderArguments componentsJoinedByString:@" "], stdoutFilePath], nil]];
-
- NSPipe *stderr_pipe = [NSPipe pipe];
- [bashTask setStandardError:stderr_pipe];
- NSFileHandle *stderr_file = [stderr_pipe fileHandleForReading];
- [bashTask launch];
- NSInteger pid = -1;
- if(caller != nil && [caller respondsToSelector:@selector(registerActivity:)]) {
- // register command
- pid = [bashTask processIdentifier];
- NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:pid], @"pid",
- (contextInfo)?:[NSDictionary dictionary], @"contextInfo",
- @"bashcommand", @"type",
- [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]], @"starttime",
- nil];
- [caller registerActivity:dict];
- }
-
- // Listen to ⌘. to terminate
- while(1) {
- if(![bashTask isRunning] || [bashTask processIdentifier] == 0) break;
- NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
- usleep(1000);
- if(!event) continue;
- if ([event type] == NSKeyDown) {
- unichar key = [[event characters] length] == 1 ? [[event characters] characterAtIndex:0] : 0;
- if (([event modifierFlags] & NSCommandKeyMask) && key == '.') {
- [bashTask terminate];
- userTerminated = YES;
- break;
- }
- [NSApp sendEvent:event];
- } else {
- [NSApp sendEvent:event];
- }
- }
-
- [bashTask waitUntilExit];
-
- // unregister BASH command if it was registered
- if(pid > 0) {
- [caller removeRegisteredActivity:pid];
- }
-
- // Remove files
- [fm removeItemAtPath:scriptFilePath error:nil];
- if([theEnv objectForKey:SPBundleShellVariableQueryFile])
- [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableQueryFile] error:nil];
- if([theEnv objectForKey:SPBundleShellVariableQueryResultFile])
- [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableQueryResultFile] error:nil];
- if([theEnv objectForKey:SPBundleShellVariableQueryResultStatusFile])
- [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableQueryResultStatusFile] error:nil];
- if([theEnv objectForKey:SPBundleShellVariableQueryResultMetaFile])
- [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableQueryResultMetaFile] error:nil];
- if([theEnv objectForKey:SPBundleShellVariableInputTableMetaData])
- [fm removeItemAtPath:[theEnv objectForKey:SPBundleShellVariableInputTableMetaData] error:nil];
-
- // If return from bash re-activate Sequel Pro
- [NSApp activateIgnoringOtherApps:YES];
-
- NSInteger status = [bashTask terminationStatus];
- NSData *errdata = [stderr_file readDataToEndOfFile];
-
- // Check STDERR
- if([errdata length] && (status < SPBundleRedirectActionNone || status > SPBundleRedirectActionLastCode)) {
- [fm removeItemAtPath:stdoutFilePath error:nil];
-
- if(status == 9 || userTerminated) return @"";
- if(theError != NULL) {
- NSMutableString *errMessage = [[[NSMutableString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] autorelease];
- [errMessage replaceOccurrencesOfString:[NSString stringWithFormat:@"%@: ", scriptFilePath] withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [errMessage length])];
- *theError = [[[NSError alloc] initWithDomain:NSPOSIXErrorDomain
- code:status
- userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- errMessage,
- NSLocalizedDescriptionKey,
- nil]] autorelease];
- } else {
- NSBeep();
- }
- return @"";
- }
-
- // Read STDOUT saved to file
- if([fm fileExistsAtPath:stdoutFilePath isDirectory:nil]) {
- NSString *stdoutContent = [NSString stringWithContentsOfFile:stdoutFilePath encoding:NSUTF8StringEncoding error:nil];
- if(bashTask) [bashTask release], bashTask = nil;
- [fm removeItemAtPath:stdoutFilePath error:nil];
- if(stdoutContent != nil) {
- if (status == 0) {
- return stdoutContent;
- } else {
- if(theError != NULL) {
- if(status == 9 || userTerminated) return @"";
- NSMutableString *errMessage = [[[NSMutableString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] autorelease];
- [errMessage replaceOccurrencesOfString:SPBundleTaskScriptCommandFilePath withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [errMessage length])];
- *theError = [[[NSError alloc] initWithDomain:NSPOSIXErrorDomain
- code:status
- userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- errMessage,
- NSLocalizedDescriptionKey,
- nil]] autorelease];
- } else {
- NSBeep();
- }
- if(status > SPBundleRedirectActionNone && status <= SPBundleRedirectActionLastCode)
- return stdoutContent;
- else
- return @"";
- }
- } else {
- NSLog(@"Couldn't read return string from “%@” by using UTF-8 encoding.", self);
- NSBeep();
+ NSUInteger idx = [geomStr length] - 1;
+
+ while (idx > 1)
+ {
+ if ([geomStr characterAtIndex:idx] == ')') break;
+
+ idx--;
}
+
+ return [NSString stringWithFormat:@"GeomFromText('%@'%@)", [geomStr substringToIndex:idx + 1], [geomStr substringFromIndex:idx + 1]];
}
-
- if (bashTask) [bashTask release];
- [fm removeItemAtPath:stdoutFilePath error:nil];
- return @"";
-}
-
-/**
- * Run self as BASH command(s) and return the result.
- * This task can be interrupted by pressing ⌘.
- *
- * @param shellEnvironment A dictionary of environment variable values whose keys are the variable names.
- *
- * @param path The current directory for the bash command. If path is nil, the current directory is inherited from the process that created the receiver (normally /).
- *
- * @param theError If not nil and the bash command failed it contains the returned error message as NSLocalizedDescriptionKey
- *
- */
-- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path error:(NSError**)theError
-{
- return [self runBashCommandWithEnvironment:shellEnvironment atCurrentDirectoryPath:path callerInstance:nil contextInfo:nil error:theError];
}
-#endif
/**
* Returns the minimum of a, b and c.
*/
-- (NSInteger)smallestOf:(NSInteger)a andOf:(NSInteger)b andOf:(NSInteger)c
+- (NSInteger)_smallestOf:(NSInteger)a andOf:(NSInteger)b andOf:(NSInteger)c
{
NSInteger min = a;
diff --git a/Source/SPTextView.m b/Source/SPTextView.m
index 5466bd83..ac9bf3a2 100644
--- a/Source/SPTextView.m
+++ b/Source/SPTextView.m
@@ -40,8 +40,10 @@
#ifndef SP_REFACTOR /* headers */
#import "SPAppController.h"
#endif
-#import <SPMySQL/SPMySQL.h>
#import "SPDatabaseStructure.h"
+#import "SPBundleCommandRunner.h"
+
+#import <SPMySQL/SPMySQL.h>
#pragma mark -
#pragma mark lex init
@@ -1853,7 +1855,7 @@ NSInteger _alphabeticSort(id string1, id string2, void *reverse)
NSRange cmdRange = [theHintString rangeOfRegex:@"(?s)(?<!\\\\)\\$\\(\\s*(.*)\\s*\\)" capture:1L];
if(cmdRange.length) {
NSError *err = nil;
- NSString *cmdResult = [[theHintString substringWithRange:cmdRange] runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&err];
+ NSString *cmdResult = [SPBundleCommandRunner runBashCommand:[theHintString substringWithRange:cmdRange] withEnvironment:nil atCurrentDirectoryPath:nil error:&err];
if(err == nil) {
[theHintString replaceCharactersInRange:tagRange withString:cmdResult];
} else if([err code] != 9) { // Suppress an error message if command was killed
diff --git a/Source/SPTextViewAdditions.m b/Source/SPTextViewAdditions.m
index 8c5043cf..e8eb838e 100644
--- a/Source/SPTextViewAdditions.m
+++ b/Source/SPTextViewAdditions.m
@@ -35,6 +35,7 @@
#import "SPTextView.h"
#import "SPWindowController.h"
#import "SPDatabaseDocument.h"
+#import "SPBundleCommandRunner.h"
@implementation NSTextView (SPTextViewAdditions)
@@ -651,15 +652,14 @@
return;
}
- NSString *output = [cmd runBashCommandWithEnvironment:env
- atCurrentDirectoryPath:nil
- callerInstance:[(SPAppController*)[NSApp delegate] frontDocument]
- contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name",
- NSLocalizedString(@"Input Field", @"input field menu item label"), @"scope",
- uuid, SPBundleFileInternalexecutionUUID,
- nil]
- error:&err];
+ NSString *output = [SPBundleCommandRunner runBashCommand:cmd withEnvironment:env
+ atCurrentDirectoryPath:nil
+ callerInstance:[(SPAppController*)[NSApp delegate] frontDocument]
+ contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
+ ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name",
+ NSLocalizedString(@"Input Field", @"input field menu item label"), @"scope",
+ uuid, SPBundleFileInternalexecutionUUID, nil]
+ error:&err];
[[NSFileManager defaultManager] removeItemAtPath:bundleInputFilePath error:nil];
diff --git a/UnitTests/SPStringAdditionsTest.h b/UnitTests/SPStringAdditionsTest.h
index 66570b35..786df76f 100644
--- a/UnitTests/SPStringAdditionsTest.h
+++ b/UnitTests/SPStringAdditionsTest.h
@@ -27,4 +27,6 @@
@interface SPStringAdditionsTest : SenTestCase
+- (void)testStringByRemovingCharactersInSet;
+
@end
diff --git a/UnitTests/SPStringAdditionsTest.m b/UnitTests/SPStringAdditionsTest.m
index 7f151b3a..b0143a0f 100644
--- a/UnitTests/SPStringAdditionsTest.m
+++ b/UnitTests/SPStringAdditionsTest.m
@@ -34,7 +34,7 @@ static NSString *SPUTFTestString = @"In der Kürze liegt die Würz";
/**
* stringByRemovingCharactersInSet test case.
*/
-/*- (void)testStringByRemovingCharactersInSet
+- (void)testStringByRemovingCharactersInSet
{
NSString *charsToRemove = @"abc',ü";
@@ -55,6 +55,6 @@ static NSString *SPUTFTestString = @"In der Kürze liegt die Würz";
expectedUTFString,
@"The following characters should have been removed %@",
charsToRemove);
-}*/
+}
@end
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index 21ae7fae..79ab2e70 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -35,6 +35,7 @@
1713C740140D8AEF00CFD461 /* SPQueryDocumentsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1713C73F140D8AEF00CFD461 /* SPQueryDocumentsController.m */; };
1713C75F140D8D5900CFD461 /* SPQueryConsoleDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 1713C75E140D8D5900CFD461 /* SPQueryConsoleDataSource.m */; };
17148565125F5FF500321285 /* SPDatabaseCharacterSets.m in Sources */ = {isa = PBXBuildFile; fileRef = 17148564125F5FF500321285 /* SPDatabaseCharacterSets.m */; };
+ 1717F9661557E0450065C036 /* SPStringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1789343B0F30C1DD0097539A /* SPStringAdditions.m */; };
17292443107AC41000B21980 /* SPXMLExporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 17292442107AC41000B21980 /* SPXMLExporter.m */; };
172A65110F7BED7A001E861A /* SPConsoleMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 172A65100F7BED7A001E861A /* SPConsoleMessage.m */; };
173284EA1088FEDE0062E892 /* SPConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 173284E91088FEDE0062E892 /* SPConstants.m */; };
@@ -65,6 +66,7 @@
176059B913361D390098E162 /* SPTableCopyTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 112730551180788A000737FD /* SPTableCopyTest.m */; };
176059BA13361D3A0098E162 /* SPDatabaseCopyTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1198F5C31174EF3F00670590 /* SPDatabaseCopyTest.m */; };
1761FD480EF03A6F00331368 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1761FD460EF03A6F00331368 /* MainMenu.xib */; };
+ 176E14D115570FE300FAF326 /* SPBundleCommandRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 176E14D015570FE300FAF326 /* SPBundleCommandRunner.m */; };
177E792E0FCB54EC00E9E122 /* database-small.png in Resources */ = {isa = PBXBuildFile; fileRef = 177E792B0FCB54EC00E9E122 /* database-small.png */; };
177E792F0FCB54EC00E9E122 /* dummy-small.png in Resources */ = {isa = PBXBuildFile; fileRef = 177E792C0FCB54EC00E9E122 /* dummy-small.png */; };
177E79300FCB54EC00E9E122 /* table-small-square.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 177E792D0FCB54EC00E9E122 /* table-small-square.tiff */; };
@@ -667,6 +669,8 @@
1760599E1336199D0098E162 /* SPMenuAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMenuAdditionsTests.m; sourceTree = "<group>"; };
1761FD470EF03A6F00331368 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
1761FD9D0EF0488900331368 /* build-version.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "build-version.pl"; sourceTree = "<group>"; };
+ 176E14CF15570FE300FAF326 /* SPBundleCommandRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPBundleCommandRunner.h; sourceTree = "<group>"; };
+ 176E14D015570FE300FAF326 /* SPBundleCommandRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPBundleCommandRunner.m; sourceTree = "<group>"; };
177E792B0FCB54EC00E9E122 /* database-small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "database-small.png"; sourceTree = "<group>"; };
177E792C0FCB54EC00E9E122 /* dummy-small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dummy-small.png"; sourceTree = "<group>"; };
177E792D0FCB54EC00E9E122 /* table-small-square.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "table-small-square.tiff"; sourceTree = "<group>"; };
@@ -1463,6 +1467,8 @@
children = (
BC68BFC5128D4EAE004907D9 /* SPBundleEditorController.h */,
BC68BFC6128D4EAE004907D9 /* SPBundleEditorController.m */,
+ 176E14CF15570FE300FAF326 /* SPBundleCommandRunner.h */,
+ 176E14D015570FE300FAF326 /* SPBundleCommandRunner.m */,
BC77C5E2129AA69E009AD832 /* SPBundleHTMLOutputController.h */,
BC77C5E3129AA69E009AD832 /* SPBundleHTMLOutputController.m */,
);
@@ -3074,6 +3080,7 @@
1798F1C4155018E2004B0AB8 /* SPMutableArrayAdditionsTest.m in Sources */,
17DB5F441555CA300046834B /* SPMutableArrayAdditions.m in Sources */,
17DB5F4A1555CA810046834B /* SPMenuAdditions.m in Sources */,
+ 1717F9661557E0450065C036 /* SPStringAdditions.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3297,6 +3304,7 @@
1798F19B1550185B004B0AB8 /* SPTreeNode.m in Sources */,
1798F19E15501892004B0AB8 /* SPFlippedView.m in Sources */,
17D5B49E1553059F00EF3BB3 /* SPViewCopy.m in Sources */,
+ 176E14D115570FE300FAF326 /* SPBundleCommandRunner.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};