OK, let's say I'm creating a (Bash) Terminal emulator - I'm not actually, but it's pretty close in terms of description.
I've managed to get (almost) everything working, however I'm facing one simple issue: maintaining the current directory.
I mean... let's say the user runs pwd and we execute this via NSTask and /usr/bin/env bash. This outputs the current app's directory. That's fine.
Now, let's say the user enters cd ... The path is changing right? (OK, even for that particular session, but it is changing, nope?)
So, I though of storing the task's currentDirectoryPath when the Task is terminated and then re-set it when starting another Bash-related task.
However, it keeps getting the very same path (the one the app bundle is in).
What am I missing?
Any ideas on how to get this working?
The code
- (NSString*)exec:(NSArray *)args environment:(NSDictionary*)env action:(void (^)(NSString*))action completed:(void (^)(NSString*))completed
{
_task = [NSTask new];
_output = [NSPipe new];
_error = [NSPipe new];
_input = [NSPipe new];
NSFileHandle* outputF = [_output fileHandleForReading];
NSFileHandle* errorF = [_error fileHandleForReading];
__block NSString* fullOutput = @"";
NSMutableDictionary* envs = [NSMutableDictionary dictionary];
envs[@"PATH"] = [[APP environment] envPath];
[_task setLaunchPath:@"/usr/bin/env"];
if (env)
{
for (NSString* key in env)
{
envs[key] = env[key];
}
}
[_task setEnvironment:envs];
[_task setArguments:args];
[_task setStandardOutput:_output];
[_task setStandardError:_error];
[_task setStandardInput:_input];
void (^outputter)(NSFileHandle*) = ^(NSFileHandle *file){
NSData *data = [file availableData];
NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
action(str);
fullOutput = [fullOutput stringByAppendingString:str];
};
[outputF setReadabilityHandler:outputter];
[errorF setReadabilityHandler:outputter];
[_task setTerminationHandler:^(NSTask* task){
completed(fullOutput);
dispatch_async(dispatch_get_main_queue(), ^{
[[APP environment] setPwd:[task currentDirectoryPath]];
});
[task.standardOutput fileHandleForReading].readabilityHandler = nil;
[task.standardError fileHandleForReading].readabilityHandler = nil;
[task.standardInput fileHandleForWriting].writeabilityHandler = nil;
[task terminate];
task = nil;
}];
if (![[[APP environment] pwd] isEqualToString:@""])
[_task setCurrentDirectoryPath:[[APP environment] pwd]];
[_task launch];
return @"";
}
I am fairly certain that running
cdinNSTaskdoes not change the value oftask.currentDirectoryPath. Have you tried setting a break point in your dispatch call to see if that value is actually being set correctly?Edit:
From your termination handler try doing
[[APP environment] setPwd:[[[NSProcessInfo processInfo]environment]objectForKey:@"PATH"]];