/* ** PipeDelegate.m ** ** Copyright (c) 2008-2016 Free Software Foundation ** ** Author: Gregory Casamento ** Riccardo Mottola ** ** 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 2 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, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include /* for stderr and perror*/ #include /* for int errno */ #include #include #include #include #include #import "PipeDelegate.h" #import "PCDebugger.h" #ifndef NOTIFICATION_CENTER #define NOTIFICATION_CENTER [NSNotificationCenter defaultCenter] #endif @implementation PipeDelegate - (id)init { if ((self = [super init])) { userInputColor = [[NSColor blueColor] retain]; debuggerColor = [[NSColor blackColor] retain]; messageColor = [[NSColor brownColor] retain]; errorColor = [[NSColor redColor] retain]; } return self; } - (NSTextView *)textView { return tView; } - (void)setTextView: (NSTextView *)tv { if (tView != tv) { [tView release]; tView = tv; [tView retain]; } } - (PCDebugger *)debugger { return debugger; } - (void)setDebugger:(PCDebugger *)dbg { if (debugger != dbg) { [debugger release]; debugger = dbg; [debugger retain]; } } - (NSColor *)userInputColor { return userInputColor; } - (NSColor *)debuggerColor { return debuggerColor; } - (NSColor *)messageColor { return messageColor; } - (NSColor *)errorColor { return errorColor; } /** * Log string to the view. */ - (void) logString:(NSString *)str newLine:(BOOL)newLine withColor:(NSColor *)color { NSMutableDictionary *textAttributes; NSAttributedString *attrStr; if (newLine) { str = [str stringByAppendingString:@"\n"]; } textAttributes = [NSMutableDictionary dictionary]; [textAttributes setObject:[NSFont userFixedPitchFontOfSize:0] forKey:NSFontAttributeName]; if (color) { [textAttributes setObject:color forKey:NSForegroundColorAttributeName]; } attrStr = [[NSAttributedString alloc] initWithString: str attributes: textAttributes]; [[tView textStorage] appendAttributedString: attrStr]; [attrStr release]; [tView scrollRangeToVisible:NSMakeRange([[tView string] length], 0)]; [tView setNeedsDisplay:YES]; } /** * Log standard out. */ - (void) logStdOut:(NSNotification *)aNotif { NSData *data; NSFileHandle *handle = stdoutHandle; if ((data = [handle availableData]) && [data length] > 0) { NSString *dataString; dataString = [[NSString alloc] initWithData:data encoding:[NSString defaultCStringEncoding]]; [self logString: dataString newLine: NO withColor:debuggerColor]; RELEASE(dataString); } if (task) { [handle waitForDataInBackgroundAndNotify]; } else { [NOTIFICATION_CENTER removeObserver: self name: NSFileHandleDataAvailableNotification object: handle]; } } /** * Log error out. */ - (void) logErrOut:(NSNotification *)aNotif { NSData *data; NSFileHandle *handle = error_handle; if ((data = [handle availableData]) && [data length] > 0) { NSString *dataString; dataString = [[NSString alloc] initWithData:data encoding:[NSString defaultCStringEncoding]]; [self logString: dataString newLine: NO withColor:errorColor]; RELEASE(dataString); } if (task) { [handle waitForDataInBackgroundAndNotify]; } else { [NOTIFICATION_CENTER removeObserver:self name: NSFileHandleDataAvailableNotification object: handle]; } } /** * Notified when the task is completed. */ - (void) taskDidTerminate: (NSNotification *)notif { NSLog(@"Task Terminated..."); [self logString: [self stopMessage] newLine:YES withColor:messageColor]; } /** * Message to print when the task starts */ - (NSString *) startMessage { return @"=== Task Started ==="; } /** * Message to print when the task stops */ - (NSString *) stopMessage { return @"\n=== Task Stopped ==="; } /** * Start the program. */ - (void) runProgram: (NSString *)path inCurrentDirectory: (NSString *)directory withArguments: (NSArray *)array logStandardError: (BOOL)logError { NSPipe *inPipe; NSPipe *outPipe; task = [[NSTask alloc] init]; [task setArguments: array]; [task setCurrentDirectoryPath: directory]; [task setLaunchPath: path]; inPipe = [NSPipe pipe]; outPipe = [NSPipe pipe]; stdinHandle = [[inPipe fileHandleForWriting] retain]; stdoutHandle = [[outPipe fileHandleForReading] retain]; [task setStandardOutput: outPipe]; [task setStandardInput: inPipe]; [stdoutHandle waitForDataInBackgroundAndNotify]; // Log standard error, if requested. if(logError) { [task setStandardError: [NSPipe pipe]]; error_handle = [[task standardError] fileHandleForReading]; [error_handle waitForDataInBackgroundAndNotify]; [NOTIFICATION_CENTER addObserver:self selector:@selector(logErrOut:) name:NSFileHandleDataAvailableNotification object:error_handle]; } // set up notifications to get data. [NOTIFICATION_CENTER addObserver:self selector:@selector(logStdOut:) name:NSFileHandleDataAvailableNotification object:stdoutHandle]; [NOTIFICATION_CENTER addObserver:self selector:@selector(taskDidTerminate:) name:NSTaskDidTerminateNotification object:task]; // run the task... NS_DURING { [self logString: [self startMessage] newLine:YES withColor:messageColor]; [task launch]; } NS_HANDLER { NSRunAlertPanel(@"Problem Launching Debugger", [localException reason], @"OK", nil, nil, nil); NSLog(@"Task Terminated Unexpectedly..."); [self logString: @"\n=== Task Terminated Unexpectedly ===\n" newLine:YES withColor:messageColor]; //Clean up after task is terminated [[NSNotificationCenter defaultCenter] postNotificationName: NSTaskDidTerminateNotification object: task]; } NS_ENDHANDLER } - (void) terminate { if(task) { [task terminate]; } } - (void) dealloc { [NOTIFICATION_CENTER removeObserver: self]; [self terminate]; [userInputColor release]; [debuggerColor release]; [messageColor release]; [errorColor release]; [tView release]; [super dealloc]; } - (void) putString: (NSString *)string; { unichar *str = (unichar *)[string cStringUsingEncoding: [NSString defaultCStringEncoding]]; int len = strlen((char *)str); NSData *data = [NSData dataWithBytes: str length: len]; [stdinHandle writeData: data]; [stdinHandle synchronizeFile]; } /* for input as typed from the user */ - (void) typeString: (NSString *)string { NSUInteger strLen; strLen = [string length]; [self putString:string]; // if we have a single backspace or delete character if (strLen == 1 && [string characterAtIndex:strLen-1] == '\177') { NSUInteger textLen; textLen = [[tView string] length]; [tView setSelectedRange:NSMakeRange(textLen-1, 1)]; [tView delete:nil]; return; } [self logString:string newLine:NO withColor:userInputColor]; } /** * Put a single character into the stream. */ - (void) putChar:(unichar)ch { NSData *data = [NSData dataWithBytes: &ch length: 1]; [stdinHandle writeData: data]; } - (void) interrupt { [task interrupt]; } /** * Respond to key events and pipe them down to the debugger */ - (void) keyDown: (NSEvent*)theEvent { NSString *chars; chars = [theEvent characters]; if ([chars length] == 0) { } else if ([chars length] == 1) { unichar c; c = [chars characterAtIndex: 0]; //NSLog(@"char: %d", c); if (c == 3) // ETX, Control-C { [self interrupt]; // send the interrupt signal to the subtask } else if (c == 13) // CR { [self typeString: @"\n"]; } else { [self typeString: chars]; } } else NSLog(@"characters: |%@|", chars); } @end