Skip to content

Commit

Permalink
[Issue jigish#85] add shell operation
Browse files Browse the repository at this point in the history
  • Loading branch information
Jigish Patel committed Oct 17, 2012
1 parent e3ebb14 commit 9c0906b
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 3 deletions.
18 changes: 18 additions & 0 deletions Slate.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
3BA1BA5C162DD7190026774E /* TestMathUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BA1BA5B162DD7190026774E /* TestMathUtils.m */; };
3BA1BA62162DDFEF0026774E /* TestExpressionPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BA1BA61162DDFEF0026774E /* TestExpressionPoint.m */; };
3BA1BA65162E01A30026774E /* TestStringTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BA1BA64162E01A20026774E /* TestStringTokenizer.m */; };
3BA1BA68162F3E530026774E /* ShellOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BA1BA67162F3E530026774E /* ShellOperation.m */; };
3BA1BA6B162F4CFF0026774E /* ShellUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BA1BA6A162F4CFF0026774E /* ShellUtils.m */; };
3BA1BA6E162F51760026774E /* TestShellUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BA1BA6D162F51760026774E /* TestShellUtils.m */; };
3BA7485913B1D0F500CFA792 /* MathUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BA7485813B1D0F500CFA792 /* MathUtils.m */; };
3BA85C9A16276DAB00FAAF0B /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA85C9916276DAB00FAAF0B /* Sparkle.framework */; };
3BA85C9B16276E9E00FAAF0B /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BA85C9916276DAB00FAAF0B /* Sparkle.framework */; };
Expand Down Expand Up @@ -159,6 +162,12 @@
3BA1BA61162DDFEF0026774E /* TestExpressionPoint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestExpressionPoint.m; sourceTree = "<group>"; };
3BA1BA63162E01A20026774E /* TestStringTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestStringTokenizer.h; sourceTree = "<group>"; };
3BA1BA64162E01A20026774E /* TestStringTokenizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestStringTokenizer.m; sourceTree = "<group>"; };
3BA1BA66162F3E530026774E /* ShellOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShellOperation.h; sourceTree = "<group>"; };
3BA1BA67162F3E530026774E /* ShellOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShellOperation.m; sourceTree = "<group>"; };
3BA1BA69162F4CFE0026774E /* ShellUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShellUtils.h; sourceTree = "<group>"; };
3BA1BA6A162F4CFF0026774E /* ShellUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShellUtils.m; sourceTree = "<group>"; };
3BA1BA6C162F51760026774E /* TestShellUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestShellUtils.h; sourceTree = "<group>"; };
3BA1BA6D162F51760026774E /* TestShellUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestShellUtils.m; sourceTree = "<group>"; };
3BA7485713B1D0F500CFA792 /* MathUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MathUtils.h; sourceTree = "<group>"; };
3BA7485813B1D0F500CFA792 /* MathUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MathUtils.m; sourceTree = "<group>"; };
3BA85C9916276DAB00FAAF0B /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = "<group>"; };
Expand Down Expand Up @@ -282,6 +291,8 @@
3BA1BA61162DDFEF0026774E /* TestExpressionPoint.m */,
3BA1BA5A162DD7190026774E /* TestMathUtils.h */,
3BA1BA5B162DD7190026774E /* TestMathUtils.m */,
3BA1BA6C162F51760026774E /* TestShellUtils.h */,
3BA1BA6D162F51760026774E /* TestShellUtils.m */,
3BA1BA63162E01A20026774E /* TestStringTokenizer.h */,
3BA1BA64162E01A20026774E /* TestStringTokenizer.m */,
);
Expand Down Expand Up @@ -380,6 +391,8 @@
3B7EB3A6138F3F6800EBEC2B /* ResizeOperation.m */,
3BE702B1161F9D5000260716 /* SequenceOperation.h */,
3BE702B2161F9D5000260716 /* SequenceOperation.m */,
3BA1BA66162F3E530026774E /* ShellOperation.h */,
3BA1BA67162F3E530026774E /* ShellOperation.m */,
3BDDA17814FD95E200829D2A /* SnapshotOperation.h */,
3BDDA17914FD95E200829D2A /* SnapshotOperation.m */,
3B9B301F151C830E0069D95E /* SwitchAppQuittingOverlayView.h */,
Expand Down Expand Up @@ -417,6 +430,8 @@
3BB5380313AEDA190005CFFC /* ScreenState.m */,
3B66A56013ADBDFD0015EDD5 /* ScreenWrapper.h */,
3B66A56113ADBDFD0015EDD5 /* ScreenWrapper.m */,
3BA1BA69162F4CFE0026774E /* ShellUtils.h */,
3BA1BA6A162F4CFF0026774E /* ShellUtils.m */,
3B58154215054EBD0078D568 /* SlateLogger.h */,
3B7EB4CA138F72D900EBEC2B /* StringTokenizer.h */,
3B7EB4CB138F72D900EBEC2B /* StringTokenizer.m */,
Expand Down Expand Up @@ -685,6 +700,8 @@
3BE702B3161F9D5000260716 /* SequenceOperation.m in Sources */,
3BF57C9E16216C2200C0F89B /* VisibilityOperation.m in Sources */,
3B883A0D1627725200FF3D8C /* RelaunchOperation.m in Sources */,
3BA1BA68162F3E530026774E /* ShellOperation.m in Sources */,
3BA1BA6B162F4CFF0026774E /* ShellUtils.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -698,6 +715,7 @@
3BA1BA5C162DD7190026774E /* TestMathUtils.m in Sources */,
3BA1BA62162DDFEF0026774E /* TestExpressionPoint.m in Sources */,
3BA1BA65162E01A30026774E /* TestStringTokenizer.m in Sources */,
3BA1BA6E162F51760026774E /* TestShellUtils.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
3 changes: 3 additions & 0 deletions Slate/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ extern NSString *const HIDE;
extern NSString *const SHOW;
extern NSString *const TOGGLE;
extern NSString *const RELAUNCH;
extern NSString *const SHELL;

// Parameters and Options
extern NSString *const CENTER;
Expand Down Expand Up @@ -227,6 +228,8 @@ extern NSString *const KEYBOARD_LAYOUT_DVORAK;
extern NSString *const PADDING;
extern NSString *const CURRENT;
extern NSString *const ALL_BUT;
extern NSString *const WAIT;
extern NSString *const PATH;

// Directions and Anchors
extern NSString *const UP;
Expand Down
3 changes: 3 additions & 0 deletions Slate/Constants.m
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
NSString *const SHOW = @"show";
NSString *const TOGGLE = @"toggle";
NSString *const RELAUNCH = @"relaunch";
NSString *const SHELL = @"shell";

// Parameters and Options
NSString *const CENTER = @"center";
Expand Down Expand Up @@ -226,6 +227,8 @@
NSString *const PADDING = @"padding";
NSString *const CURRENT = @"current";
NSString *const ALL_BUT = @"all-but:";
NSString *const WAIT = @"wait";
NSString *const PATH = @"path:";

// Directions and Anchors
NSString *const UP = @"up";
Expand Down
3 changes: 3 additions & 0 deletions Slate/Operation.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#import "SequenceOperation.h"
#import "VisibilityOperation.h"
#import "RelaunchOperation.h"
#import "ShellOperation.h"

@implementation Operation

Expand Down Expand Up @@ -101,6 +102,8 @@ + (id)operationFromString:(NSString *)opString {
operation = [VisibilityOperation visibilityOperationFromString:opString];
} else if ([op isEqualToString:RELAUNCH]) {
operation = [RelaunchOperation relaunchOperationFromString:opString];
} else if ([op isEqualToString:SHELL]) {
operation = [ShellOperation shellOperationFromString:opString];
} else {
SlateLogger(@"ERROR: Unrecognized operation '%@'", opString);
@throw([NSException exceptionWithName:@"Unrecognized Operation" reason:[NSString stringWithFormat:@"Unrecognized operation '%@' in '%@'", op, opString] userInfo:nil]);
Expand Down
39 changes: 39 additions & 0 deletions Slate/ShellOperation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// ShellOperation.h
// Slate
//
// Created by Jigish Patel on 10/17/12.
// Copyright 2012 Jigish Patel. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses

#import "Operation.h"

@interface ShellOperation : Operation {
NSString *command;
NSArray *args;
NSString *currentPath;
BOOL waitForExit;
}

@property NSString *command;
@property NSArray *args;
@property NSString *currentPath;
@property BOOL waitForExit;

- (id)initWithCommand:(NSString *)theCommand args:(NSArray *)theArgs waitForExit:(BOOL)theWaitForExit currentPath:(NSString *)theCurrentPath;

+ (id)shellOperationFromString:(NSString *)shellOperation;

@end
99 changes: 99 additions & 0 deletions Slate/ShellOperation.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// ShellOperation.m
// Slate
//
// Created by Jigish Patel on 10/17/12.
// Copyright 2012 Jigish Patel. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses

#import "ShellOperation.h"
#import "Constants.h"
#import "SlateLogger.h"
#import "StringTokenizer.h"
#import "ShellUtils.h"

@implementation ShellOperation

@synthesize command, args, waitForExit, currentPath;

- (id)initWithCommand:(NSString *)theCommand args:(NSArray *)theArgs waitForExit:(BOOL)theWaitForExit currentPath:(NSString *)theCurrentPath {
self = [super init];
if (self) {
[self setCommand:theCommand];
[self setArgs:theArgs];
[self setWaitForExit:theWaitForExit];
[self setCurrentPath:theCurrentPath];
}
return self;
}

- (BOOL)doOperation {
SlateLogger(@"----------------- Begin Shell Operation -----------------");
// We don't use the passed in AccessibilityWrapper or ScreenWrapper so they are nil. No need to waste time creating them here.
BOOL success = [self doOperationWithAccessibilityWrapper:nil screenWrapper:nil];
SlateLogger(@"----------------- End Shell Operation -----------------");
return success;
}

- (BOOL)doOperationWithAccessibilityWrapper:(AccessibilityWrapper *)iamnil screenWrapper:(ScreenWrapper *)iamalsonil {
NSTask *task = [ShellUtils run:[self command] args:[self args] wait:[self waitForExit] path:[self currentPath]];
return task != nil;
}

- (BOOL)testOperation {
return [ShellUtils commandExists:[self command]];
}

+ (id)shellOperationFromString:(NSString *)shellOperation {
// shell [wait] 'command'
NSMutableArray *tokens = [[NSMutableArray alloc] initWithCapacity:10];
[StringTokenizer tokenize:shellOperation into:tokens quoteChars:[NSCharacterSet characterSetWithCharactersInString:QUOTES]];

if ([tokens count] < 2) {
SlateLogger(@"ERROR: Invalid Parameters '%@'", shellOperation);
@throw([NSException exceptionWithName:@"Invalid Parameters" reason:[NSString stringWithFormat:@"Invalid Parameters in '%@'. Shell operations require the following format: shell [wait] 'command'", shellOperation] userInfo:nil]);
}

BOOL waitForExit = NO;
NSString *currentPath = nil;
for (NSInteger i = 1; i < [tokens count] - 1; i++) {
if ([[tokens objectAtIndex:i] isEqualToString:WAIT]) {
waitForExit = YES;
} else if ([[tokens objectAtIndex:i] hasPrefix:PATH]) {
currentPath = [[tokens objectAtIndex:i] stringByReplacingOccurrencesOfString:PATH withString:EMPTY];
if ([currentPath hasPrefix:TILDA]) {
currentPath = [currentPath stringByExpandingTildeInPath];
}
}
}
NSString *commandAndArgs = [tokens lastObject];
NSMutableArray *commandAndArgsTokens = [NSMutableArray array];
[StringTokenizer tokenize:commandAndArgs into:commandAndArgsTokens];
if ([commandAndArgsTokens count] < 1) {
SlateLogger(@"ERROR: Invalid Parameters '%@'", shellOperation);
@throw([NSException exceptionWithName:@"Invalid Parameters" reason:[NSString stringWithFormat:@"Invalid Parameters in '%@'. Shell operations require the following format: shell [wait] 'command'", shellOperation] userInfo:nil]);
}
NSString *command = [commandAndArgsTokens objectAtIndex:0];
NSMutableArray *args = [NSMutableArray array];
for (NSInteger i = 1; i < [commandAndArgsTokens count]; i++) {
[args addObject:[commandAndArgsTokens objectAtIndex:i]];
}

Operation *op = [[ShellOperation alloc] initWithCommand:command args:args waitForExit:waitForExit currentPath:currentPath];
return op;

}

@end
28 changes: 28 additions & 0 deletions Slate/ShellUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// ShellUtils.h
// Slate
//
// Created by Jigish Patel on 10/17/12.
// Copyright 2012 Jigish Patel. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses

#import <Foundation/Foundation.h>

@interface ShellUtils : NSObject

+ (BOOL)commandExists:(NSString *)command;
+ (NSTask *)run:(NSString *)command args:(NSArray *)args wait:(BOOL)wait path:(NSString *)path;

@end
81 changes: 81 additions & 0 deletions Slate/ShellUtils.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// ShellUtils.m
// Slate
//
// Created by Jigish Patel on 10/17/12.
// Copyright 2012 Jigish Patel. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses

#import "ShellUtils.h"
#import "Constants.h"

@implementation ShellUtils

+ (BOOL)commandExists:(NSString *)command {
if (command == nil || [EMPTY isEqualToString:command]) return NO;
@try {
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath:@"/usr/bin/command"];

NSArray *arguments;
arguments = [NSArray arrayWithObjects:@"-v", command, nil];
[task setArguments:arguments];

NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
[task setStandardInput:[NSPipe pipe]];

NSFileHandle *file;
file = [pipe fileHandleForReading];

[task launch];

NSData *data;
data = [file readDataToEndOfFile];

NSString *string;
string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
if ([string isEqualToString:EMPTY]) {
return NO;
}
return YES;
} @catch (id ex) {
return NO;
}
}

+ (NSTask *)run:(NSString *)command args:(NSArray *)args wait:(BOOL)wait path:(NSString *)path {
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath:command];
[task setArguments:args];
if (path != nil) [task setCurrentDirectoryPath:path];

NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
[task setStandardInput:[NSPipe pipe]];

NSFileHandle *file;
file = [pipe fileHandleForReading];

[task launch];
if (wait) [task waitUntilExit];
return task;
}

@end
1 change: 1 addition & 0 deletions Slate/StringTokenizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
+ (BOOL)isSpaceChar:(unichar)c;
+ (BOOL)isQuoteChar:(unichar)c quoteChars:(NSCharacterSet *)quoteChars;
+ (void)tokenize:(NSString *)s into:(NSMutableArray *)array;
+ (void)tokenize:(NSString *)s into:(NSMutableArray *)array quoteChars:(NSCharacterSet *)quotes;
+ (void)tokenize:(NSString *)s into:(NSMutableArray *)array maxTokens:(NSInteger)maxTokens;
+ (void)tokenize:(NSString *)s into:(NSMutableArray *)array maxTokens:(NSInteger)maxTokens quoteChars:(NSCharacterSet *)quotes;
+ (void)firstToken:(NSString *)s into:(NSMutableString *)token;
Expand Down
Loading

0 comments on commit 9c0906b

Please sign in to comment.