diff --git a/ChangeLog b/ChangeLog index 6518d06..1145d56 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2016-03-23 Riccardo Mottola + + * Modules/Debuggers/ProjectCenter/PCDebugger.m + * Modules/Debuggers/ProjectCenter/PCDebuggerView.h + * Modules/Debuggers/ProjectCenter/PCDebuggerView.m + * Modules/Debuggers/ProjectCenter/PipeDelegate.h + * Modules/Debuggers/ProjectCenter/PipeDelegate.m + * Modules/Debuggers/ProjectCenter/PCDebuggerViewDelegateProtocol.h + Merge in from pipes branch: stdio/stdout over pipes handled in a separate delegate class. + 2016-03-23 Riccardo Mottola * Framework/PCBundleManager.m diff --git a/Modules/Debuggers/ProjectCenter/GNUmakefile b/Modules/Debuggers/ProjectCenter/GNUmakefile index 08d3d19..b165cb0 100644 --- a/Modules/Debuggers/ProjectCenter/GNUmakefile +++ b/Modules/Debuggers/ProjectCenter/GNUmakefile @@ -38,7 +38,9 @@ ProjectCenter_RESOURCE_FILES= \ ProjectCenter_HEADERS= \ PCDebugger.h \ PCDebugggerView.h \ - PTYView.h + PCDebuggerViewDelegateProtocol.h \ + PTYView.h \ + PipeDelegate.h # # Class files @@ -46,10 +48,11 @@ ProjectCenter_HEADERS= \ ProjectCenter_OBJC_FILES= \ PCDebugger.m \ PCDebuggerView.m \ - PTYView.m + PTYView.m \ + PipeDelegate.m -ADDITIONAL_NATIVE_LIBS += util +#ADDITIONAL_NATIVE_LIBS += util include ../../GNUmakefile.bundles include $(GNUSTEP_MAKEFILES)/bundle.make diff --git a/Modules/Debuggers/ProjectCenter/PCDebugger.m b/Modules/Debuggers/ProjectCenter/PCDebugger.m index 5058b9d..31f9025 100644 --- a/Modules/Debuggers/ProjectCenter/PCDebugger.m +++ b/Modules/Debuggers/ProjectCenter/PCDebugger.m @@ -1,9 +1,10 @@ /* ** PCDebugger ** -** Copyright (c) 2008-2015 +** Copyright (c) 2008-2016 ** -** Author: Gregory Casamento +** 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 @@ -25,6 +26,8 @@ #import "PCDebuggerView.h" #import "Modules/Preferences/EditorFSC/PCEditorFSCPrefs.h" +#import "PCDebuggerViewDelegateProtocol.h" +#import "PipeDelegate.h" #ifndef NOTIFICATION_CENTER #define NOTIFICATION_CENTER [NSNotificationCenter defaultCenter] @@ -122,6 +125,7 @@ static NSImage *downImage = nil; { if((self = [super init]) != nil) { + id viewDelegate; // initialization here... if([NSBundle loadNibNamed: @"PCDebugger" owner: self] == NO) { @@ -129,6 +133,10 @@ static NSImage *downImage = nil; } [(PCDebuggerView *)debuggerView setDebugger:self]; + viewDelegate = [[PipeDelegate alloc] init]; + [debuggerView setDelegate:viewDelegate]; + [viewDelegate setTextView:debuggerView]; + [viewDelegate release]; } return self; } @@ -152,7 +160,7 @@ static NSImage *downImage = nil; { [debuggerView runProgram: debuggerPath inCurrentDirectory: [path stringByDeletingLastPathComponent] - withArguments: [[NSArray alloc] initWithObjects: @"-f", path, nil] + withArguments: [[NSArray alloc] initWithObjects: @"-f", path, nil] logStandardError: YES]; } diff --git a/Modules/Debuggers/ProjectCenter/PCDebuggerView.h b/Modules/Debuggers/ProjectCenter/PCDebuggerView.h index 7ce9c23..45ee294 100644 --- a/Modules/Debuggers/ProjectCenter/PCDebuggerView.h +++ b/Modules/Debuggers/ProjectCenter/PCDebuggerView.h @@ -1,9 +1,10 @@ /* ** PCDebuggerView ** -** Copyright (c) 2008 +** Copyright (c) 2008-2016 ** -** Author: Gregory Casamento +** 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 @@ -20,20 +21,32 @@ ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#import "PTYView.h" +#import +#import + +#import "PCDebuggerViewDelegateProtocol.h" @class PCDebugger; -@class NSString; -@interface PCDebuggerView : PTYView +@interface PCDebuggerView : NSTextView { PCDebugger *debugger; + id viewDelegate; NSString *currentFile; int subProcessId; } - (void) setDebugger:(PCDebugger *)theDebugger; +- (void) setDelegate:(id ) vd; - (void) setCurrentFile: (NSString *)fileName; - (NSString *) currentFile; - (int) subProcessId; + +- (void) runProgram: (NSString *)path + inCurrentDirectory: (NSString *)directory + withArguments: (NSArray *)array + logStandardError: (BOOL)logError; + +- (void) putString: (NSString *)string; + @end diff --git a/Modules/Debuggers/ProjectCenter/PCDebuggerView.m b/Modules/Debuggers/ProjectCenter/PCDebuggerView.m index 7c2a7a7..6a30ca9 100644 --- a/Modules/Debuggers/ProjectCenter/PCDebuggerView.m +++ b/Modules/Debuggers/ProjectCenter/PCDebuggerView.m @@ -1,9 +1,10 @@ /* ** PCDebuggerView ** -** Copyright (c) 2008 +** Copyright (c) 2008-2016 ** -** Author: Gregory Casamento +** 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 @@ -40,6 +41,17 @@ debugger = theDebugger; } +- (void) setDelegate:(id ) vd +{ + if (viewDelegate != vd) + { + [viewDelegate release]; + viewDelegate = vd; + [viewDelegate retain]; + } +} + + /** * Log string to the view. */ @@ -144,7 +156,7 @@ // if the line is not filtered, print it... if(printLine) { - [super logString: str newLine: newLine]; + [viewDelegate logString: str newLine: newLine withColor:[viewDelegate debuggerColor]]; } } @@ -158,38 +170,6 @@ return currentFile; } -/** - * lookup the process id. - */ -/* -- (int) subProcessId -{ - int task_pid = [task processIdentifier]; - int child_pid = 0; - NSArray *entries = [[NSFileManager defaultManager] directoryContentsAtPath: @"/proc"]; - NSEnumerator *en = [entries objectEnumerator]; - NSString *entry = nil; - - // FIXME: I'm looking for a generic way to do this, what we have here is very /proc specific. - // which I don't like since it ties this functionality to systems which have /proc. - while((entry = [en nextObject]) != nil) - { - int pid = [entry intValue]; - if (pid != 0) - { - int ppid = getppid(pid); - if (ppid == task_pid) - { - child_pid = pid; - break; - } - } - } - - return child_pid; -} -*/ - - (int) subProcessId { return subProcessId; @@ -204,15 +184,41 @@ kill(pid,SIGINT); #endif } + [super putString:@"-exec-interrupt"]; } - (void) terminate { - [super terminate]; + [viewDelegate terminate]; } - (void) mouseDown: (NSEvent *)event { // do nothing... } + +/** + * Start the program. + */ +- (void) runProgram: (NSString *)path + inCurrentDirectory: (NSString *)directory + withArguments: (NSArray *)array + logStandardError: (BOOL)logError +{ + [viewDelegate runProgram: path + inCurrentDirectory: directory + withArguments: array + logStandardError: logError]; +} + +- (void) putString: (NSString *)string +{ + [viewDelegate putString:string]; +} + +- (void) keyDown: (NSEvent*)theEvent +{ + [viewDelegate keyDown:theEvent]; +} + @end diff --git a/Modules/Debuggers/ProjectCenter/PCDebuggerViewDelegateProtocol.h b/Modules/Debuggers/ProjectCenter/PCDebuggerViewDelegateProtocol.h new file mode 100644 index 0000000..5586eaf --- /dev/null +++ b/Modules/Debuggers/ProjectCenter/PCDebuggerViewDelegateProtocol.h @@ -0,0 +1,55 @@ +/* +** PCDebuggerViewDelegateProtocol.h +** +** Copyright (c) 2016 +** +** Author: 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 +*/ + +@class NSColor; +@class NSTextView; +@class NSArray; +@class NSString; + +@protocol PCDebuggerViewDelegateProtocol + +- (NSColor *)userInputColor; +- (NSColor *)debuggerColor; +- (NSColor *)messageColor; +- (NSColor *)errorColor; + +- (NSTextView *)textView; +- (void)setTextView: (NSTextView *)tv; + +- (void) runProgram: (NSString *)path + inCurrentDirectory: (NSString *)directory + withArguments: (NSArray *)array + logStandardError: (BOOL)logError; + +- (void)logString:(NSString *)str + newLine:(BOOL)newLine + withColor:(NSColor *)color; + +- (void) terminate; + +- (void) interrupt; + +- (void) putString: (NSString *)string; + +- (void) keyDown: (NSEvent*)theEvent; + +@end diff --git a/Modules/Debuggers/ProjectCenter/PipeDelegate.h b/Modules/Debuggers/ProjectCenter/PipeDelegate.h new file mode 100644 index 0000000..5efdae5 --- /dev/null +++ b/Modules/Debuggers/ProjectCenter/PipeDelegate.h @@ -0,0 +1,55 @@ +/* +** PipeDelegate +** +** Copyright (c) 2008-2016 +** +** 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 +*/ + +#import +#import + +#import "PCDebuggerViewDelegateProtocol.h" + +@interface PipeDelegate : NSObject +{ + NSTextView *tView; + NSTask *task; + NSFileHandle *stdinHandle; + NSFileHandle *stdoutHandle; + NSFileHandle *error_handle; + + NSColor *userInputColor; + NSColor *debuggerColor; + NSColor *messageColor; + NSColor *errorColor; +} + +- (void)logStdOut:(NSNotification *)aNotif; + +- (void)logErrOut:(NSNotification *)aNotif; + +- (void) taskDidTerminate: (NSNotification *)notif; + +- (NSString *) startMessage; + +- (NSString *) stopMessage; + +- (void) putChar:(unichar)ch; + +@end diff --git a/Modules/Debuggers/ProjectCenter/PipeDelegate.m b/Modules/Debuggers/ProjectCenter/PipeDelegate.m new file mode 100644 index 0000000..f795454 --- /dev/null +++ b/Modules/Debuggers/ProjectCenter/PipeDelegate.m @@ -0,0 +1,395 @@ +/* +** 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" + +#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]; + } +} + +- (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 diff --git a/Modules/GNUmakefile b/Modules/GNUmakefile index 4474d84..8960ac7 100644 --- a/Modules/GNUmakefile +++ b/Modules/GNUmakefile @@ -41,11 +41,7 @@ SUBPROJECTS = \ Preferences/Misc \ Preferences/EditorFSC -# Do not compile the Debuggers/ProjectCenter module on MinGW since I'm -# told at the moment it doesn't even compile there. -ifneq ($(GNUSTEP_TARGET_OS), mingw32) - SUBPROJECTS += Debuggers/ProjectCenter -endif +SUBPROJECTS += Debuggers/ProjectCenter include $(GNUSTEP_MAKEFILES)/aggregate.make