* Modules/Debuggers/ProjectCenter/GNUmakefile: Added PTYView.[hm]

* Modules/Debuggers/ProjectCenter/PCDebugger.[hm]: Simplified to 
	call PCDebuggerView instance with the program to start.
	* Modules/Debuggers/ProjectCenter/PCDebuggerView.[hm]: Refactored
	into a subclass of PTYView.
	* Modules/Debuggers/ProjectCenter/PTYView.[hm]: pty based terminal
	view which allows us to read and write from a pseudo-terminal.
	The code in the master/slave methods was inspired by an example
	given in "UNIX Network Programming" by W. Richard Stevens and also
	Nicola Vitacolonna.
	* Modules/Debuggers/ProjectCenter/Resources/PCDebugger.gorm: Added
	PTYView and made the PCDebuggerView a subclass of it.   Also made
	the window release when closed so that the gdb process gets 
	terminated.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/apps/projectcenter/trunk@27416 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Gregory John Casamento 2008-12-25 15:46:41 +00:00
parent cd2924d9ab
commit 43e24cad84
10 changed files with 550 additions and 245 deletions

View file

@ -1,3 +1,20 @@
2008-12-25 10:30-EST Gregory John Casamento <greg_casamento@yahoo.com>
* Modules/Debuggers/ProjectCenter/GNUmakefile: Added PTYView.[hm]
* Modules/Debuggers/ProjectCenter/PCDebugger.[hm]: Simplified to
call PCDebuggerView instance with the program to start.
* Modules/Debuggers/ProjectCenter/PCDebuggerView.[hm]: Refactored
into a subclass of PTYView.
* Modules/Debuggers/ProjectCenter/PTYView.[hm]: pty based terminal
view which allows us to read and write from a pseudo-terminal.
The code in the master/slave methods was inspired by an example
given in "UNIX Network Programming" by W. Richard Stevens and also
Nicola Vitacolonna.
* Modules/Debuggers/ProjectCenter/Resources/PCDebugger.gorm: Added
PTYView and made the PCDebuggerView a subclass of it. Also made
the window release when closed so that the gdb process gets
terminated.
2008-12-22 21:51-EST Gregory John Casamento <greg_casamento@yahoo.com>
* Modules/Debuggers/ProjectCenter/PCDebugger.m: Write to

View file

@ -27,14 +27,16 @@ ProjectCenter_RESOURCE_FILES= \
#
ProjectCenter_HEADERS= \
PCDebugger.h \
PCDebugggerView.h
PCDebugggerView.h \
PTYView.h
#
# Class files
#
ProjectCenter_OBJC_FILES= \
PCDebugger.m \
PCDebuggerView.m
PCDebuggerView.m \
PTYView.m
include ../../GNUmakefile.bundles
include $(GNUSTEP_MAKEFILES)/bundle.make

View file

@ -1,4 +1,24 @@
/* All Rights reserved */
/*
** PCDebugger
**
** Copyright (c) 2008
**
** Author: Gregory Casamento <greg_casamento@yahoo.com>
**
** 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 <stdio.h>
@ -13,17 +33,8 @@
id debuggerWindow;
NSString *path;
NSString *debuggerPath;
NSTask *debuggerTask;
NSPipe *standardInput;
NSPipe *standardOutput;
NSPipe *standardError;
NSFileHandle *readHandle;
NSFileHandle *errorReadHandle;
BOOL _isLogging;
BOOL _isErrorLogging;
FILE *stdInStream;
// BOOL _isLogging;
// BOOL _isErrorLogging;
// FILE *std_input;
}
- (void)putChar:(unichar)ch;
@end

View file

@ -1,4 +1,24 @@
/* All Rights reserved */
/*
** PCDebugger
**
** Copyright (c) 2008
**
** Author: Gregory Casamento <greg_casamento@yahoo.com>
**
** 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 <AppKit/AppKit.h>
#include "PCDebugger.h"
@ -25,7 +45,7 @@
}
-(void) debugExecutableAtPath: (NSString *)filePath
withDebugger: (NSString *)debugger
withDebugger: (NSString *)debugger
{
ASSIGN(path,filePath);
ASSIGN(debuggerPath,debugger);
@ -39,210 +59,18 @@
[self startDebugger];
}
- (void)logErrorString:(NSString *)string
{
/*
NSArray *items;
items = [self parseErrorLine:string];
if (items)
{
[errorArray addObjectsFromArray:items];
[errorOutput reloadData];
[errorOutput scrollRowToVisible:[errorArray count]-1];
}
*/
}
- (void)logString:(NSString *)str
error:(BOOL)yn
newLine:(BOOL)newLine
{
NSTextView *out = debuggerView;
[out replaceCharactersInRange:
NSMakeRange([[out string] length],0) withString:str];
if (newLine)
{
[out replaceCharactersInRange:
NSMakeRange([[out string] length], 0) withString:@"\n"];
}
else
{
[out replaceCharactersInRange:
NSMakeRange([[out string] length], 0) withString:@" "];
}
[out scrollRangeToVisible:NSMakeRange([[out string] length], 0)];
[out setNeedsDisplay:YES];
}
- (void)logData:(NSData *)data
error:(BOOL)yn
{
NSString *dataString;
// NSRange newLineRange;
// NSRange lineRange;
// NSString *lineString;
dataString = [[NSString alloc]
initWithData:data
encoding:[NSString defaultCStringEncoding]];
// Process new data
/*
lineRange.location = 0;
[errorString appendString:dataString];
while (newLineRange.location != NSNotFound)
{
newLineRange = [errorString rangeOfString:@"\n"];
if (newLineRange.location < [errorString length])
{
lineRange.length = newLineRange.location+1;
lineString = [errorString substringWithRange:lineRange];
[errorString deleteCharactersInRange:lineRange];
// [self parseBuildLine:lineString];
// if (yn)
// {
// [self logErrorString:lineString];
// }
[self logString:lineString error:yn newLine:NO];
}
else
{
newLineRange.location = NSNotFound;
continue;
}
}
*/
[self logString:dataString error:yn newLine:NO];
RELEASE(dataString);
}
- (void)logStdOut:(NSNotification *)aNotif
{
NSData *data;
if ((data = [readHandle availableData]) && [data length] > 0)
{
[self logData:data error:NO];
}
if (debuggerTask)
{
[readHandle waitForDataInBackgroundAndNotify];
}
else
{
_isLogging = NO;
[NOTIFICATION_CENTER removeObserver:self
name:NSFileHandleDataAvailableNotification
object:readHandle];
}
}
- (void)logErrOut:(NSNotification *)aNotif
{
NSData *data;
if ((data = [errorReadHandle availableData]) && [data length] > 0)
{
[self logData:data error:YES];
}
if (debuggerTask)
{
[errorReadHandle waitForDataInBackgroundAndNotify];
}
else
{
_isErrorLogging = NO;
[NOTIFICATION_CENTER removeObserver:self
name:NSFileHandleDataAvailableNotification
object:errorReadHandle];
}
}
- (void) startDebugger
{
int descriptor = 0;
standardInput = [NSPipe pipe];
standardOutput = [NSPipe pipe];
standardError = [NSPipe pipe];
descriptor = [[standardInput fileHandleForWriting] fileDescriptor];
stdInStream = fdopen(descriptor, "w");
readHandle = [standardOutput fileHandleForReading];
[readHandle waitForDataInBackgroundAndNotify];
[NOTIFICATION_CENTER addObserver:self
selector:@selector(logStdOut:)
name:NSFileHandleDataAvailableNotification
object:readHandle];
_isLogging = YES;
standardError = [NSPipe pipe];
errorReadHandle = [standardError fileHandleForReading];
[errorReadHandle waitForDataInBackgroundAndNotify];
[NOTIFICATION_CENTER addObserver:self
selector:@selector(logErrOut:)
name:NSFileHandleDataAvailableNotification
object:errorReadHandle];
_isErrorLogging = YES;
// [statusField setStringValue:buildStatus];
// Run make task
[debuggerView setString:@""];
[NOTIFICATION_CENTER addObserver:self
selector:@selector(debuggerDidTerminate:)
name:NSTaskDidTerminateNotification
object:nil];
debuggerTask = [[NSTask alloc] init];
[debuggerTask setArguments: [[NSArray alloc] initWithObjects: @"--args", path, nil]];
[debuggerTask setCurrentDirectoryPath: [path stringByDeletingLastPathComponent]];
[debuggerTask setLaunchPath: debuggerPath];
[debuggerTask setStandardOutput: standardOutput];
[debuggerTask setStandardError: standardError];
[debuggerTask setStandardInput: standardInput];
NS_DURING
{
[self logString: @"\n=== Debugger Started ===\n" error: NO newLine:YES];
[debuggerTask launch];
}
NS_HANDLER
{
NSRunAlertPanel(@"Problem Launching Debugger",
[localException reason],
@"OK", nil, nil, nil);
//Clean up after task is terminated
[[NSNotificationCenter defaultCenter]
postNotificationName:NSTaskDidTerminateNotification
object:debuggerTask];
}
NS_ENDHANDLER
[debuggerView runProgram: debuggerPath
inCurrentDirectory: [path stringByDeletingLastPathComponent]
withArguments: [[NSArray alloc] initWithObjects: @"--args", path, nil]
logStandardError: YES];
}
- (void) debuggerDidTerminate: (NSNotification *)notif
{
NSLog(@"Debugger Terminated...");
[self logString: @"\n=== Debugger Terminated ===\n" error: NO newLine:YES];
}
- (void) awakeFromNib
{
[debuggerView setFont: [NSFont userFixedPitchFontOfSize: 0]];
[debuggerWindow setFrameAutosaveName: @"PCDebuggerWindow"];
}
- (NSWindow *)debuggerWindow
@ -252,7 +80,7 @@
- (void)setDebuggerWindow: (NSWindow *)window
{
ASSIGN(debuggerWindow,window);
debuggerWindow = window;
}
- (NSView *)debuggerView
@ -262,7 +90,7 @@
- (void)setDebuggerView: (id)view
{
ASSIGN(debuggerView,view);
debuggerView = view;
}
- (NSString *)path
@ -274,12 +102,4 @@
{
ASSIGN(path,p);
}
- (void)putChar:(unichar)ch
{
NSData *data = [NSData dataWithBytes: &ch length: 1];
NSFileHandle *fh = [standardInput fileHandleForWriting];
[fh writeData: data];
// fputc(ch, stdInStream);
}
@end

View file

@ -1,10 +1,30 @@
/* All Rights reserved */
/*
** PCDebuggerView
**
** Copyright (c) 2008
**
** Author: Gregory Casamento <greg_casamento@yahoo.com>
**
** 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 <AppKit/AppKit.h>
#include "PTYView.h"
#import "PCDebugger.h"
@class PCDebugger;
@interface PCDebuggerView : NSTextView
@interface PCDebuggerView : PTYView
{
PCDebugger *debugger;
}

View file

@ -1,31 +1,47 @@
/* All Rights reserved */
/*
** PCDebuggerView
**
** Copyright (c) 2008
**
** Author: Gregory Casamento <greg_casamento@yahoo.com>
**
** 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 <AppKit/AppKit.h>
#include "PCDebuggerView.h"
@implementation PCDebuggerView
/** set the controlling debugger instance */
-(void)setDebugger:(PCDebugger *)theDebugger
{
debugger = theDebugger;
}
/** respond to key events and pipe them down to the debugger */
-(BOOL)performKeyEquivalent: (NSEvent*)theEvent
/**
* Log string to the view.
*/
- (void) logString:(NSString *)str
newLine:(BOOL)newLine
{
NSString *chars;
chars = [theEvent characters];
if ([chars length] == 1)
NSRange range;
// FIXME: Filter this error, until we find a better way to deal with it.
range = [str rangeOfString: @"[tcsetpgrp failed in terminal_inferior:"];
if (range.location == NSNotFound)
{
unichar c;
c = [chars characterAtIndex: 0];
NSLog(@"character: %c", c);
[debugger putChar:c];
}
return YES; // [super performKeyEquivalent:theEvent];
[super logString: str newLine: newLine];
}
}
@end

View file

@ -0,0 +1,62 @@
/*
** PTYView
**
** Copyright (c) 2008
**
** Author: Gregory Casamento <greg_casamento@yahoo.com>
**
** 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 <Foundation/Foundation.h>
#include <AppKit/AppKit.h>
@interface PTYView : NSTextView
{
char pty_name[12]; /* "/dev/[pt]tyXY" = 10 chars + null byte */
NSTask *task;
NSFileHandle *master_handle;
NSFileHandle *slave_handle;
NSFileHandle *error_handle;
}
- (int)master;
- (int)slave: (int)master;
- (void)logString:(NSString *)str
newLine:(BOOL)newLine;
- (void)logData:(NSData *)data;
- (void)logStdOut:(NSNotification *)aNotif;
- (void)logErrOut:(NSNotification *)aNotif;
- (void) taskDidTerminate: (NSNotification *)notif;
- (NSString *) startMessage;
- (NSString *) stopMessage;
- (void) runProgram: (NSString *)path
inCurrentDirectory: (NSString *)directory
withArguments: (NSArray *)array
logStandardError: (BOOL)logError;
- (void) terminate;
- (void) putChar:(unichar)ch;
@end

View file

@ -0,0 +1,350 @@
/*
** PTYView
**
** Copyright (c) 2008
**
** Author: Gregory Casamento <greg_casamento@yahoo.com>
**
** 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 <PTYView.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#ifndef NOTIFICATION_CENTER
#define NOTIFICATION_CENTER [NSNotificationCenter defaultCenter]
#endif
@implementation PTYView
/**
* Instantiate this view.
*/
- (id) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if(self != nil)
{
// initialize the pty name field.
strcpy(pty_name, "/dev/ptyXY");
}
return self;
}
/**
* Creates master device.
*/
- (int) master
{
struct stat buff;
static char hex[] = "0123456789abcdef";
static char pty[] = "pqrs";
int i, fd;
char *p;
for (p = pty; *p != 0; p++)
{
pty_name[8] = *p;
pty_name[9] = '0';
if (stat(pty_name, &buff) < 0)
{
break;
}
for (i = 0; i < 16; i++)
{
pty_name[9] = hex[i];
if ((fd = open(pty_name, O_RDWR)) >= 0)
{
return(fd);
}
}
}
return(-1);
}
/**
* Open the slave half of a pseudo-terminal.
*/
- (int) slave:(int)master_fd
{
int fd;
// change to t, for slave tty.
pty_name[5] = 't';
if ((fd = open(pty_name, O_RDWR)) < 0)
{
close(master_fd);
return(-1);
}
return(fd);
}
/**
* Log string to the view.
*/
- (void) logString:(NSString *)str
newLine:(BOOL)newLine
{
NSRange range;
[self replaceCharactersInRange:
NSMakeRange([[self string] length],0) withString:str];
if (newLine)
{
[self replaceCharactersInRange:
NSMakeRange([[self string] length], 0) withString:@"\n"];
}
//
// Is it backspace? If so, remove one character from the terminal to reflect
// the deletion. For some reason backspace sends "\b \b", so I have to remove
// four characters in order to appropriately delete from the buffer.
//
range = [str rangeOfString: @"\b"];
if (range.location != NSNotFound)
{
NSString *newString = [[self string] substringToIndex: [[self string] length] - 4];
[self setString: newString];
}
[self scrollRangeToVisible:NSMakeRange([[self string] length], 0)];
[self setNeedsDisplay:YES];
}
/**
* Log data.
*/
- (void) logData:(NSData *)data
{
NSString *dataString;
dataString = [[NSString alloc]
initWithData:data
encoding:[NSString defaultCStringEncoding]];
[self logString: dataString newLine: NO];
RELEASE(dataString);
}
/**
* Log standard out.
*/
- (void) logStdOut:(NSNotification *)aNotif
{
NSData *data;
NSFileHandle *handle = master_handle;
if ((data = [handle availableData]) && [data length] > 0)
{
[self logData: data];
}
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)
{
// [self logString: @"\n" newLine: NO];
[self logData: data];
}
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];
}
/**
* Message to print when the task starts
*/
- (NSString *) startMessage
{
return @"=== Task Started ===";
}
/**
* Message to print when the task stops
*/
- (NSString *) stopMessage
{
return @"=== Task Stopped ===";
}
/**
* Start the program.
*/
- (void) runProgram: (NSString *)path
inCurrentDirectory: (NSString *)directory
withArguments: (NSArray *)array
logStandardError: (BOOL)logError
{
int master_fd, slave_fd;
task = [[NSTask alloc] init];
[task setArguments: array];
[task setCurrentDirectoryPath: directory];
[task setLaunchPath: path];
master_fd = [self master];
if(master_fd > 0)
{
slave_fd = [self slave: master_fd];
if(slave_fd > 0)
{
slave_handle = [[NSFileHandle alloc] initWithFileDescriptor: slave_fd];
master_handle = [[NSFileHandle alloc] initWithFileDescriptor: master_fd];
[task setStandardOutput: slave_handle];
[task setStandardInput: slave_handle];
[master_handle 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:master_handle];
[NOTIFICATION_CENTER addObserver:self
selector:@selector(taskDidTerminate:)
name:NSTaskDidTerminateNotification
object:nil];
// run the task...
NS_DURING
{
[self logString: [self startMessage]
newLine:YES];
[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];
//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];
[super dealloc];
}
/**
* Put a single character into the stream.
*/
- (void) putChar:(unichar)ch
{
NSData *data = [NSData dataWithBytes: &ch length: 1];
[master_handle writeData: data];
}
/**
* Respond to key events and pipe them down to the debugger
*/
- (void) keyDown: (NSEvent*)theEvent
{
NSString *chars;
chars = [theEvent characters];
if ([chars length] == 1)
{
unichar c;
c = [chars characterAtIndex: 0];
if (c == 3) // ETX, Control-C
{
[task interrupt]; // send the interrupt signal to the subtask
}
else
{
[self putChar: c];
}
}
}
@end

View file

@ -10,6 +10,13 @@
Super = NSObject;
};
PCDebuggerView = {
Actions = (
);
Outlets = (
);
Super = PTYView;
};
PTYView = {
Actions = (
);
Outlets = (