views:

162

answers:

4
task = [NSTask new];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:[NSArray arrayWithObject:@"/applications/jarvis/brain/server.sh"]];
[task setCurrentDirectoryPath:@"/"];

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

[task launch];

NSMutableString *outputString = [NSMutableString string];
while ([outputString rangeOfString:@"Jarvis>"].location == NSNotFound) {
    [outputString appendString:[[[NSString alloc] initWithData:[[outputPipe fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding] autorelease]];
     }

NSArray* substrings = [outputString componentsSeparatedByString:@"Jarvis>"];
NSString* finalCharlieOutputNSTask = [substrings lastObject];
NSSpeechSynthesizer * syn = [[NSSpeechSynthesizer alloc] init];
[syn startSpeakingString:finalCharlieOutputNSTask]; 
self.charlieOutput.stringValue = finalCharlieOutputNSTask;

Ok, so that's my code. It launches an SH file and reads the output. BUT, I want it to wait until "Jarvis>" appears in the string before saying and printing the result. But, it seems like with the while loop, my code freezes there. Without it it reads the normal output of launching the server.sh file, but the whole thing. Any ideas why this doesn't work?

Here is the Server.sh file:

echo Starting Jarvis Program D.
ALICE_HOME=.
SERVLET_LIB=lib/servlet.jar
ALICE_LIB=lib/aliceserver.jar
JS_LIB=lib/js.jar

# Set SQL_LIB to the location of your database driver.
SQL_LIB=lib/mysql_comp.jar

# These are for Jetty; you will want to change these if you are using a different http server.
 HTTP_SERVER_LIBS=lib/org.mortbay.jetty.jar

 PROGRAMD_CLASSPATH=$SERVLET_LIB:$ALICE_LIB:$JS_LIB:$SQL_LIB:$HTTP_SERVER_LIBS
 java -classpath $PROGRAMD_CLASSPATH -Xms64m -Xmx128m org.alicebot.server.net.AliceServer $1
+1  A: 

You want to put the readDataToEndOfFile inside the loop. Otherwise, it reads the data once, checks for existence of your string, then loops forever if it doesn't find it in that first reading.

Amorya
+1  A: 

Elijah, edit your code to look like this:

...
[task launch];

NSMutableString *outputString = [NSMutableString string];
while ([outputString rangeOfString:@"Jarvis>"].location == NSNotFound) {
    [outputString appendString:[[[NSString alloc] initWithData:[[outputPipe fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding] autorelease];
}

if ([task isRunning]) {
    [task terminate];
}

NSArray *subStrings = [outputString componentsSeparatedByString:@"Jarvis>"];
...

This code within the loop will successively read output data from your file and append it to the current output, and stop reading when @"Jarvis>" is found (it will also kill the task after a @"Jarvis>" is found, so it doesn't keep running forever).

itaiferber
BAH! No, it still freezes at the while loop. I'm posting my current code in my question. Any ideas?
Elijah W.
Also, like I said before: It seems that, when I check what the output string is before the while loop I get "Starting program G" which is an echo in the .sh file to clarify that it's started. BUT, during the while loop, I check the value of the output string, and it returns the same thing...no other setup...just the echo. I'm checking it with NSRunAlertPanel()
Elijah W.
Besides the original `echo`, how else is your bash file printing to stdout? Are there any other `echo`'s that are supposed to run? What is the output when you run the bash file from the Terminal? Is the user supposed to enter any input?
itaiferber
Well, it's a java AIML interpreter for ALICE AI. I wanted to make a GUI for it. Server.sh just runs the bot. And, yes there is user input. I'm guessing that it's not outputting anything else except echo because the other stuff is setup. Well, until it has a line for the bot's response and for User input (all run through terminal)
Elijah W.
Well, of course it's not printing anything. If the rest of the bash file is just setup, it's not going to print anything. Can you please post up the bash file so I can see how you're running it? You're going to have to set up multiple `NSTask`s if you're expecting to run a program like this, especially through a GUI...
itaiferber
added it to my question
Elijah W.
Well then, I have no idea how you intend to communicate with Alice through the terminal. I'm not a Java programmer, so I don't know the specifics of how the Alice server runs, or how it outputs its data, or what the last line in the bash file really does, but from what I can tell, you're going to have to separate calling the actual Alice server into a separate task with input and output pipes to the user. Sorry, but that's all I can do for you...
itaiferber
Ok I understand. Thanks for your help! Can you show an exampleOf that?
Elijah W.
Take a look at http://pastie.org/1091333, I hope it helps you out...
itaiferber
THANKS!!! One quick question: // Before running the above code, you're going to have to precalculate the class path and store it, and the location of Alice (or whatever the $1 means in your bash file). <-what do you mean by this?? the path to where the alice folder is?
Elijah W.
Also, do I need to have user input in the setup?
Elijah W.
No, the setup, from what I can tell, will require no user input. And what I meant was, $1 in your bash file meant some input the user put in. I wasn't sure what it would be, so I just assumed it had something to do with where Alice was located, but I had no way of knowing...
itaiferber
Ok. I did all that and it's still returning nothing. Not even the echo. Do I need to launch the setup task?
Elijah W.
I don't know, I'm sorry. There's a certain point past which I can't bother to deal with an issue. Maybe you can find a different way to handle this. I'm just not sure.
itaiferber
I understand... Thanks for what you've done!
Elijah W.
I'm sorry, I wish I could help more.
itaiferber
No problem! I'll mess around with it and if I find something I'll post it as an answer.
Elijah W.
+1  A: 

Maybe try flushing NSPipe along the lines of code published in the article:

"NSTasks, NSPipes, and deadlocks when reading...",

http://dev.notoptimal.net/2007/04/nstasks-nspipes-and-deadlocks-when.html

Yet another approach could test the use of NSTask & NSUnbufferedIO.

You will find some example code in the book "Cocoa Programming" by Don Yacktman.

# http://www.cocoaprogramming.net
# http://www.cocoaprogramming.net/CocoaProgramming-20021010.tgz

open -e CocoaProgramming-20021010/Chapter\ 24/Animal/AnimalController.h \
        CocoaProgramming-20021010/Chapter\ 24/Animal/AnimalController.m \
        CocoaProgramming-20021010/Chapter\ 24/Calendar/CalendarController.m

# for yet another try with sample code using waitForDataInBackgroundAndNotify see:
# http://cocoawithlove.com/2009/05/invoking-other-processes-in-cocoa.html

open -e OpenFileKiller/NSTask+OneLineTasksWithOutput.m
# --> [standardOutputFile waitForDataInBackgroundAndNotify];
franco
A: 

For non-blocking IO code for NSTask & NSFileManager also see the source code of DOCtor:

# http://www.stone.com/DOCtor/
# http://www.stone.com/DOCtor/DOCtor.tar.gz

open -e DOCTor/NSFileHandle_CFRNonBlockingIO.h \
        DOCTor/NSFileHandle_CFRNonBlockingIO.m \
        DOCTor/NSFileManager-Extensions.h \
        DOCTor/NSFileManager-Extensions.m

Also note that many command line programs (internally) block buffer their output to stdout if stdout is a pipe and not an interactive terminal (tty). For this reason some command line programs have special options to line-buffer their output regardless of sending output to a pipe or tty.

tcpdump -l
grep --line-buffered
...
ceeit