NSTask, NSPipe's and interactive UNIX command
NSTask, NSPipe's and interactive UNIX command
- Subject: NSTask, NSPipe's and interactive UNIX command
- From: Nicola Vitacolonna <email@hidden>
- Date: Wed, 2 Jan 2002 15:40:01 +0100
I have a problem with a wrapper I wrote for an interactive UNIX command.
The only such example I was able to find on the net is the source code
of TeXShop, which works fine.
The essential code is as follows (suppose that "command" takes a
filename as argument). In the interface I declared the following
variables:
NSPipe *outputPipe;
NSPipe *inputPipe;
NSFileHandle *readHandle;
NSFileHandle *writeHandle;
NSTask *theTask;
and in the implementation:
- (void)runTheTask:(NSString *)aFile {
NSMutableArray *args;
outputPipe = [[NSPipe pipe] retain];
readHandle = [outputPipe fileHandleForReading];
inputPipe = [[NSPipe pipe] retain];
writeHandle = [inputPipe fileHandleForWriting];
theTask = [[NSTask alloc] init];
[theTask setStandardOutput:outputPipe];
[theTask setStandardError:outputPipe];
[theTask setStandardInput:inputPipe];
[readHandle readInBackgroundAndNotify];
/* Set arguments */
/* The syntax is: command <filename> */
args = [NSMutableArray array];
[args addObject:aFile];
[theTask setLaunchPath:@"/usr/bin/command"];
[theTask setArguments:args];
[theTask launch];
}
In initialization, I registered the following method to respond to a
NSFileHandleNotificationDataItem notification:
// Receives output from command
- (void)outputAvailable: (NSNotification *)aNotification
{
NSData *taskData;
NSString *newOutput;
taskData = [[aNotification userInfo] objectForKey:
@"NSFileHandleNotificationDataItem"];
if ([taskData length]) {
newOutput = [[NSString alloc] initWithData: taskData
encoding:NSMacOSRomanStringEncoding];
/* Here goes the code to show newOutput to the user */
[newOutput release];
[readHandle readInBackgroundAndNotify];
}
}
Finally, the following method receives input from the interface and
sends it to "command":
- (IBAction)putStringIntoInputPipe:(id)sender {
NSData *myData;
NSString *userInput;
userInput = [[sender stringValue] stringByAppendingString:@"\n"];
if (inputPipe) {
myData = [userInput dataUsingEncoding:
NSMacOSRomanStringEncoding allowLossyConversion:YES];
[writeHandle writeData: myData];
}
}
So far, so good. Some information about what "command" does may be
useful too: "command" prints some text on screen (using a sequence of
printf()) and then waits for a character to be pressed (by calling
getchar()) before printing some other output. Something like that:
for (i=0;i<100; i++)
printf("aaaaaaaaaaaaaaaaaaaaaaaaa");
getchar();
printf("zzzzzzzzzzzzzzzzzzzzzzzzz");
The problem is that notifications of available data on the output pipe
are fired only *after* "command" has exited! That is, the behaviour of
this code is as follows: "command" is run but no output is shown;
however, if the user presses a key, this is correctly sent to the input
pipe, so "command" (which was hanging on getchar()) goes on, writes
some other text and exits. At this point only notifications of available
data are sent, so the program displays all the output of "command",
before and after getchar()! I do not understand why. Can someone give me
some hint? Am I doing something wrong? If this is a bug, does a
workaround exist?
Moreover, if I set "command" to be pdflatex or something like that, the
same code works just fine (well, look at TeXShop)! I didn't look at
pdflatex's source, so I don't know how it is written.
Sorry for being so verbose and thanks in advance for your suggestions,
Nicola